How to compile an Expression down to the actual result? - c#

I am building an API around a web service call using Expressions to allow a developer to specify a query and have an ExpressionVisitor convert the Expression into the query string. The request is XML with a particular element containing a query string.
For example, I can do something like this which will retrieve all checking accounts with a bank name of Bank 1 or Bank 2:
"bankname = 'Bank 1' or bankname = 'Bank 2'"
The web service can handle significantly more complex queries but I'll just stick with this for now.
So I have a class CheckingAccount:
[IntacctObject("checkingaccount")]
public class CheckingAccount : Entity
{
[IntacctElement("bankaccountid")]
public string Id { get; set; }
[IntacctElement("bankname")]
public string BankName { get; set; }
}
And an ExpressionVisitor whose primary function is to convert an expression like this:
Expression> expression = ca => ca.BankName == "Bank 1" || ca.BankName == "Bank 2"
into the query: "bankname = 'Bank 1' or bankname = 'Bank 2'"
This isn't so tough. Where things start to break down are when I introduce local variables:
var account = new CheckingAccount { BankName = "Bank 1" };
string bankName = "Bank 2";
Expression> expression = ca => ca.BankName == account.BankName || ca.BankName == bankName;
I know how to deal with a simple local variable (ie. string bankName = "Bank 2") but dealing with a the other type (var account = new CheckingAccount { BankName = "Bank 1" }) is much more complex.
At the end of the day these are the big issues that I need to figure out how to deal with right now. I know there are much more complex scenarios but I'm not so concerned with those at the moment.
Here is my expression visitor (please note the generic constraint on method CreateFilter):
internal class IntacctWebService30ExpressionVisitor : ExpressionVisitor
{
private readonly List _Filters = new List();
private IntacctWebServicev30SimpleQueryFilter _CurrentSimpleFilter;
private IntacctWebServicev30ComplexQueryFilter _CurrentComplexFilter;
private MemberExpression _CurrentMemberExpression;
public string CreateFilter(Expression> expression) where TEntity : Entity
{
Visit(expression);
string filter = string.Join(string.Empty, _Filters.Select(f => f.ToString()).ToArray());
return filter;
}
protected override Expression VisitBinary(BinaryExpression node)
{
switch (node.NodeType)
{
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
_CurrentComplexFilter = new IntacctWebServicev30ComplexQueryFilter { ExpressionType = node.NodeType };
break;
case ExpressionType.Equal:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.NotEqual:
_CurrentSimpleFilter = new IntacctWebServicev30SimpleQueryFilter { ExpressionType = node.NodeType };
break;
}
return base.VisitBinary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
var attr = node.Member.GetAttribute();
if (attr != null)
_CurrentSimpleFilter.FieldName = attr.FieldName;
else
_CurrentMemberExpression = node;
return base.VisitMember(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
object value = Expression.Lambda>(node).Compile().Invoke();
string fieldValue = extraxtFieldValue(value, node);
_CurrentSimpleFilter.FieldValue = fieldValue;
if (_CurrentComplexFilter != null)
{
if (_CurrentComplexFilter.Left == null)
{
_CurrentComplexFilter.Left = _CurrentSimpleFilter;
}
else if (_CurrentComplexFilter.Right == null)
{
_CurrentComplexFilter.Right = _CurrentSimpleFilter;
_Filters.Add(_CurrentComplexFilter);
}
else
throw new InvalidOperationException();
}
else
{
_Filters.Add(_CurrentSimpleFilter);
}
return base.VisitConstant(node);
}
private string extraxtFieldValue(object value)
{
string fieldValue;
if (value is DateTime)
fieldValue = ((DateTime)value).ToString("yyyy-MM-dd");
else if (value is string)
fieldValue = value.ToString();
else if (value.GetType().IsEnum)
{
throw new NotImplementedException();
}
else
{
// Not sure if this is the best way to do this or not but can't figure out how
// else to get a variable value.
// If we are here then we are dealing with a property, field, or variable.
// This means we must extract the value from the object.
// In order to do this we will rely on _CurrentMemberExpression
if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Property)
{
fieldValue = value.GetType().GetProperty(_CurrentMemberExpression.Member.Name).GetValue(value, null).ToString();
}
else if (_CurrentMemberExpression.Member.MemberType == MemberTypes.Field)
{
fieldValue = value.GetType().GetFields().First().GetValue(value).ToString();
}
else
{
throw new InvalidOperationException();
}
}
return fieldValue;
}
}
Please let me know if you'd like any more detail....
Thanks

Have a look at an article about this very issue from Matt Warren. He provides source for a class that does exactly this, with an explanation how does it do it. It's also included in his IQToolkit.

If you are interested in using a open source third party library to do this for you, you can check out Expression Tree Serialization. I believe it does what you are looking for.

Related

C# getting more than just last part of name from nameof [duplicate]

nameof(order.User.Age) return only Age instead of order.User.Age
What is the reason to do it in more restricted way?
If we want only last name we could do something like
public static GetLastName(this string x) {
return string.Split(x, '.').Last();
}
nameof(order.User.Age).GetLastName()
And with one operator we could get both, Age and order.User.Age. But with current implementation we can only get Age. Is there some logic behind this decision? For example, such behavior is necessary for MVC binding
Html.TextBox(nameof(order.User.Age))
Note that if you need/want the "full" name, you could do this:
$"{nameof(order)}.{nameof(User)}.{nameof(Age)}".GetLastName();
as long as all of these names are in the current scope.
Obviously in this case it's not really all that helpful (the names won't be in scope in the Razor call), but it might be if you needed, for example, the full namespace qualified name of a type for a call to Type.GetType() or something.
If the names are not in scope, you could still do the somewhat more clunky:
$"{nameof(order)}.{nameof(order.User)}.{nameof(order.User.Age)}".GetLastName();
-- although chances are at least one of those should be in scope (unless User.Age is a static property).
I had the same problem and implemented a class that acts as a replacement of the nameof() keyword in order to get the full name of the expression being supplied. It's greatly inspired from OK HOSTING answer. It's just all baked and ready to use:
public static class NameOf<TSource>
{
#region Public Methods
public static string Full(Expression<Func<TSource, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
var unaryExpression = expression.Body as UnaryExpression;
if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert)
memberExpression = unaryExpression.Operand as MemberExpression;
}
var result = memberExpression.ToString();
result = result.Substring(result.IndexOf('.') + 1);
return result;
}
public static string Full(string sourceFieldName, Expression<Func<TSource, object>> expression)
{
var result = Full(expression);
result = string.IsNullOrEmpty(sourceFieldName) ? result : sourceFieldName + "." + result;
return result;
}
#endregion
}
Using it in your code would look like:
class SpeciesFamily
{
public string Name { get; set; }
}
class Species
{
public SpeciesFamily Family { get; set; }
public string Name { get; set; }
}
class Cat
{
public Species Species { get; set; }
}
// Will return a string containing "Species.Family.Name".
var fullName = NameOf<Cat>.Full(c => c.Species.Family.Name);
// Will return a string containing "cat.Species.Name".
var fullNameWithPrefix = NameOf<Cat>.Full("cat", c => c.Species.Name);
Because it is exactly what for it've been invented. As you can read in already linked discussions, here you using the nameof operator as nameof(member-access), of the form E.I<A1…AK>, which will return:
These cases are all resolved using the rules for simple name lookup $7.6.2 or member access $7.6.4. If they succeed in binding, they must bind to one of:
A method-group. This produces an error "To specify the name of a method, you must provide its arguments".
A variable, value, parameter, constant, enumeration-member, property-access, field, event, type-parameter, namespace or type. In this case the result of the nameof operator is simply "I", which is generally the name of the symbol that the argument bound to. There are some caveats…
So in this case it, by its definition, have to evaluate all expressions before all the dots, step by step, and after that evaluate the last one to get its Name:
order.User.Age --> User.Age --> Age
Take a look at this method taken from:
https://github.com/okhosting/OKHOSTING.Data/blob/master/src/PCL/OKHOSTING.Data/Validation/MemberExpression.cs
public static string GetMemberString(System.Linq.Expressions.Expression<Func<T, object>> member)
{
if (member == null)
{
throw new ArgumentNullException("member");
}
var propertyRefExpr = member.Body;
var memberExpr = propertyRefExpr as System.Linq.Expressions.MemberExpression;
if (memberExpr == null)
{
var unaryExpr = propertyRefExpr as System.Linq.Expressions.UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == System.Linq.Expressions.ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as System.Linq.Expressions.MemberExpression;
if(memberExpr != null)
{
return memberExpr.Member.Name;
}
}
}
else
{
//gets something line "m.Field1.Field2.Field3", from here we just remove the prefix "m."
string body = member.Body.ToString();
return body.Substring(body.IndexOf('.') + 1);
}
throw new ArgumentException("No property reference expression was found.", "member");
}
Some of the important purposes of using nameof is to get the last "name" in the expression.
For example nameof parameter when throwing ArgumentNullException:
void Method(string parameter)
{
if (parameter == null) throw new ArgumentNullException(nameof(parameter));
}
MVC Action links
<%= Html.ActionLink("Sign up",
#typeof(UserController),
#nameof(UserController.SignUp))
%>
INotifyPropertyChanged
int p {
get { return this._p; }
set { this._p = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.p)); }
}
More information: https://roslyn.codeplex.com/discussions/570551

Getting sub property names strongly typed

With databinding objects to controls and grids I hated how the property names would be magic strings, so I created a very simple method as follows:
public static string GetPropertyName<PropertyType>(Expression<Func<T, PropertyType>> expressionForProperty)
{
MemberExpression expression = expressionForProperty.Body as MemberExpression;
return expression.Member.Name;
}
This lets me use code such as:
Product.GetPropertyName(m => m.Name)
to return "Name".
This works perfectly for basic objects. However if I change the method call to be:
Product.GetPropertyName(m => m.ProductCategory.Name)
This also returns "Name". But in order for the databinding to work, I would need it to return "ProductCategory.Name". Is there a way I can get to this by changing the method "GetPropertyName"?
A possible workaround would be to do this:
string test = Product.GetPropertyName(p => p.ProductCategory) + "." + ProductCategory.GetPropertyName(pc => pc.Name)
However, this isn't a neat solution.
This is a modified version of something I might have found here on StackOVerflow:
public static class GetPropertyNameExtension
{
public static string GetPropertyName<TArg, TProperty>(this Expression<Func<TArg, TProperty>> propertyExpression)
{
return propertyExpression.Body.GetMemberExpression().GetPropertyName();
}
public static string GetPropertyName<TProperty>(this Expression<Func<TProperty>> propertyExpression)
{
return propertyExpression.Body.GetMemberExpression().GetPropertyName();
}
public static string GetPropertyName(this MemberExpression memberExpression)
{
if (memberExpression == null)
{
return null;
}
if (memberExpression.Member.MemberType != MemberTypes.Property)
{
return null;
}
var child = memberExpression.Member.Name;
var parent = GetPropertyName(memberExpression.Expression.GetMemberExpression());
return parent == null ?
child
: parent + "." + child;
}
public static MemberExpression GetMemberExpression(this Expression expression)
{
if (expression is MemberExpression)
return (MemberExpression)expression;
if (expression is UnaryExpression)
return (MemberExpression)((UnaryExpression)expression).Operand;
return null;
}
}
I came up with the following which seems to work:
public static string GetComplexPropertyName<PropertyType>(Expression<Func<T, PropertyType>> expressionForProperty)
{
// get the expression body
Expression expressionBody = expressionForProperty.Body as MemberExpression;
string expressionAsString = null;
// all properties bar the root property will be "convert"
switch (expressionBody.NodeType)
{
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
UnaryExpression unaryExpression = expressionBody as UnaryExpression;
if (unaryExpression != null)
{
expressionAsString = unaryExpression.Operand.ToString();
}
break;
default:
expressionAsString = expressionBody.ToString();
break;
}
// makes ure we have got an expression
if (!String.IsNullOrWhiteSpace(expressionAsString))
{
// we want to get rid of the first operand as it will be the root type, so get the first occurence of "."
int positionOfFirstDot = expressionAsString.IndexOf('.');
if (positionOfFirstDot != -1)
{
return expressionAsString.Substring(positionOfFirstDot + 1, expressionAsString.Length - 1 - positionOfFirstDot);
}
}
return string.Empty;
}

Creating a typed ModelState.AddModelError()

In a controller I can perform DB lookups etc and add some error message associated with a model property:
public ActionResult CreateJob(CreateJobModel viewModel)
{
var call = FindCall(viewModel.CallNumber);
if (call == null)
{
ModelState.AddModelError("CallNumber", "Idiot User!");
}
}
I don't like that CallNumber is a string, when ideally it should refer directly to viewModel.CallNumber, and if I change the name of that property, it should be changed too.
How can I achieve this?
I'd imagine the code would end up something like this, which would take a property access expression:
AddModelFieldError(() => viewModel.CallNumber, "Idiot User!");
But I'm not sure how to create a method like that, or in the case where it's a sub/inner-property that needs the error message.
I would write my own generic extension method:
public static class ModelStateDictionaryHelper
{
public static void AddModelError<TViewModel>(
this ModelStateDictionary me,
Expression<Func<TViewModel, object>> lambdaExpression, string error)
{
me.AddModelError(GetPropertyName(lambdaExpression), error);
}
private static string GetPropertyName(Expression lambdaExpression)
{
IList<string> list = new List<string>();
var e = lambdaExpression;
while (true)
{
switch (e.NodeType)
{
case ExpressionType.Lambda:
e = ((LambdaExpression)e).Body;
break;
case ExpressionType.MemberAccess:
var propertyInfo = ((MemberExpression)e).Member as PropertyInfo;
var prop = propertyInfo != null
? propertyInfo.Name
: null;
list.Add(prop);
var memberExpression = (MemberExpression)e;
if (memberExpression.Expression.NodeType != ExpressionType.Parameter)
{
var parameter = GetParameterExpression(memberExpression.Expression);
if (parameter != null)
{
e = Expression.Lambda(memberExpression.Expression, parameter);
break;
}
}
return string.Join(".", list.Reverse());
default:
return null;
}
}
}
private static ParameterExpression GetParameterExpression(Expression expression)
{
while (expression.NodeType == ExpressionType.MemberAccess)
{
expression = ((MemberExpression)expression).Expression;
}
return expression.NodeType == ExpressionType.Parameter ? (ParameterExpression)expression : null;
}
}
and the usage:
ModelState.AddModelError<CreateJobModel>(x => x.CallNumber,
"some kind attention");
It looks a bit differently from the version you've asked about, but I hope it can be acceptable alternative.
As of C# 6, you can use the nameof operator.
public ActionResult CreateJob(CreateJobModel viewModel)
{
var call = FindCall(viewModel.CallNumber);
if (call == null)
{
ModelState.AddModelError(nameof(CreateJobModel.CallNumber), "Idiot User!");
}
}

String condition in List<T>

I have a class named HomeInfo
public class HomeInfo
{
public int ID {get;set;}
public string OwnerName {get;set;}
public string Address {get;set;}
public int EstimatedValue {get;set;}
}
I get data from server and i add that into List<HomeInfo> listHomeInfo
Now in my GUI I need to allow filtering results based on user input, so my client wants a textbox for Estimated Value and he wants to enter text there like '>30k and <50k' or '>50k', I parsed and converted these values and created object of class
public class ExpressionValue
{
public float? FirstDigit { get; set; }
/// <summary>
/// >, >=, <,<=
/// </summary>
public string FirstExpCondition { get; set; }
/// <summary>
/// OR, AND
/// </summary>
public string ConditionOperator { get; set; }
public float SecondDigit { get; set; }
public string SecondExpCondition { get; set; }
}
Using an ExpressionValue object I am able to create a proper condition string.
Now I am able to create a condition string like 'EstimatedValue > 30000 AND EstimatedValue < 60000' or 'EstimatedValue < 50000'
I don't know how can I effectively apply this condition on 'List listHomeInfo' since as far i know List<T>.Where() doesn't support string condition. I know a way around it is to convert the list to DataTable and use Select(string expression) method and then convert DataRow[] to List<HomeInfo>, but I think there may be a better way to achieve this.
[EDIT]
I created two methods to help me out but i am getting exception "The binary operator GreaterThan is not defined for the types 'System.Single' and 'System.Double'." when creating BinaryExpression.
public static Expression<Func<T, bool>> ParseExpressionCondition<T>(string expression, string fieldName)
{
try
{
string decimalNumRegex = #"\d+(\.\d{1,2})?";
List<string> matchPatterns = new List<string>() { ">=", ">", "<=", "<" };
ExpressionValue expValue = new ExpressionValue();
Dictionary<string, string> conditions = new Dictionary<string, string>();
var parameter = Expression.Parameter(typeof(T), typeof(T).ToString());
//var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
BinaryExpression lhs = null, rhs = null;
object objectValue = null;
string condOperator = null;
foreach (string pattern in matchPatterns)
{
Match match = Regex.Match(expression, pattern + decimalNumRegex);
if (match.Success)
{
//get digit part
double digit = double.Parse(Regex.Match(match.Value, decimalNumRegex).Value);
if (!expValue.FirstDigit.HasValue)
{
objectValue = digit;
condOperator = match.Value.Replace(digit.ToString(), "");
lhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
}
else
{
objectValue = digit;
condOperator = match.Value.Replace(digit.ToString(), "");
rhs = GetBinaryExpression(parameter, fieldName, objectValue, condOperator);
}
}
}
if (expression.ToLower().Contains("and"))
return Expression.Lambda<Func<T, bool>>(Expression.And(lhs, rhs), parameter);
else if (expression.ToLower().Contains("or"))
return Expression.Lambda<Func<T, bool>>(Expression.Or(lhs, rhs), parameter);
return null;
}
catch (Exception ex)
{
Logger.WriteLog(ex);
throw ex;
}
}
private static BinaryExpression GetBinaryExpression(ParameterExpression paraExp, string fieldName, object expressionValue, string conditionOperator)
{
try
{
BinaryExpression binExp = null;
MemberExpression expressionLeft = Expression.Property(paraExp, fieldName);
Expression expressionRight = Expression.Constant(expressionValue );
switch (conditionOperator)
{
case ">":
binExp = Expression.GreaterThan(expressionLeft, expressionRight);
break;
case ">=":
binExp = Expression.GreaterThanOrEqual(expressionLeft, expressionRight);
break;
case "<":
binExp = Expression.LessThan(expressionLeft, expressionRight);
break;
case "<=":
binExp = Expression.LessThanOrEqual(expressionLeft, expressionRight);
break;
}
return binExp;
}
catch (Exception ex)
{
throw ex;
}
}
Assuming you have parsing logic already implemented to some extent, I would suggest generating an expression tree (rather than using your own custom ExpressionValue class).
E.g. 'EstimatedValue > 30000 AND EstimatedValue < 60000' could become an expression tree of the form:
var parameter = Expression.Parameter(typeof(HomeInfo), "homeInfo");
var lhs = Expression.GreaterThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(30000));
var rhs = Expression.LessThan(Expression.Property(parameter, "EstimatedValue"), Expression.Constant(60000));
var expression = Expression.Lambda<Func<HomeInfo, bool>>(Expression.AndAlso(lhs, rhs), parameter);
The list can then be queried using the generated expression tree as follows:
var results = listHomeInfo.AsQueryable().Where(expression);
Do not reinvent the wheel: NCalc does that kind of stuff already.
With a variable named EstimatedValue and a user defined expression UserExpression, in NCalc you'd do:
myList.Where(elem => new Expression(EstimatedValue.ToString() + UserExpression).Evaluate());
In your position I'd create a mini rule engine.
So
public abstract class ExpressionBase {
public float value {get;set;}
}
public class GreaterThanExpression : ExpressionBase {}
public class LessThanExpression : ExpressionBase {}
Now as you parse the entered string you can build a list of the expressions entered and then apply them to an IQueryable in the order you want to.
Write a LINQ extention method....
public static IEnumerable<HomeInfo> PassesExpression(this IEnumerable<HomeInfo> homes, ExpressionValue expression)
{
foreach(HomeInfo home in homes)
{
bool one, two;
if(expression.FirstExpCondition == '>')
one = (home.EstimatedValue > expression.FirstDigit);
else if(expression.FirstExpCondition == '>=')
one = (home.EstimatedValue >= expression.FirstDigit);
else if(expression.FirstExpCondition == '<')
one = (home.EstimatedValue < expression.FirstDigit);
else if(expression.FirstExpCondition == '<=')
one = (home.EstimatedValue <= expression.FirstDigit);
if(expression.SecondExpCondition == '>')
two = (home.EstimatedValue > expression.SecondDigit);
else if(expression.SecondExpCondition == '>=')
two = (home.EstimatedValue >= expression.SecondDigit);
else if(expression.SecondExpCondition == '<')
two = (home.EstimatedValue < expression.SecondDigit);
else if(expression.SecondExpCondition == '<=')
two = (home.EstimatedValue <= expression.SecondDigit);
if((expression.ConditionOperator == 'OR' && (one || two)) || (expression.ConditionOperator == 'AND' && (one && two)))
yield return home;
}
}
I usually have two textboxes for value ranges. One for the minimum value, one for the maximum value. They can be empty, if the limit is not required
int? min = null
int? max = null;
int i;
if (Int32.TryParse(txtMin.Text, out i) min = i;
if (Int32.TryParse(txtMax.Text, out i) max = i;
string name = txtName.Text;
With these definitions you can combine where clauses dynamically
IEnumerable<HomeInfo> result = list;
if (min.HasValue) result = result.Where(h => h.EstimatedValue >= min.Value);
if (max.HasValue) result = result.Where(h => h.EstimatedValue <= max.Value);
if (name != "")
result = result.Where(
h => h.OwnerName.StartsWith(name, StringComparison.OrdinalIgnoreCase)
);
use LinqToObjects
List<HomeInfo> homeInfos = new List<HomeInfo>();
homeInfos.Where(x => x.EstimatedValue > 1000).Where(x => x.EstimatedValue < 10000);

reflection v expression

I was looking at another question that stated how Expression can be significantly faster than reflection since it can be precompiled to IL.
I'm not really sure how to use it though. Here is some code used in a base class for a Value Oject (in the DDD sense) where the basic idea is to use the values of all public properties to determine equality, which it gets via reflection. By using this base class, you needn't implement equality for subclasses that have Value Object.
protected virtual bool HasSameObjectSignatureAs(BaseObject compareTo)
{
var signatureProperties = GetType().GetProperties();
foreach (var property in signatureProperties)
{
var valueOfThisObject = property.GetValue(this, null);
var valueOfCompareTo = property.GetValue(compareTo, null);
if (valueOfThisObject == null && valueOfCompareTo == null) {
continue;
}
if ((valueOfThisObject == null ^ valueOfCompareTo == null) ||
(!valueOfThisObject.Equals(valueOfCompareTo))) {
return false;
}
}
How would this code be re-written using Expression?
Cheers,
Berryl
You can do this by building up a nested And expression for each property you want to compare:
protected Expression<Func<BaseObject, bool>> CreatePropertiesEqualExpression(BaseObject other)
{
if (! other.GetType().IsSubclassOf(this.GetType())) throw new ArgumentException();
var properties = this.GetType().GetProperties();
Expression trueExpr = Expression.Constant(true);
Expression thisExpr = Expression.Constant(this);
ParameterExpression paramExpr = Expression.Parameter(typeof(BaseObject), "compareTo");
Expression downCastExpr = Expression.Convert(paramExpr, other.GetType());
MethodInfo eqMethod = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Static);
Expression propCompExpr = properties.Aggregate(trueExpr, (expr, prop) =>
{
Expression thisPropExpr = Expression.Property(thisExpr, prop);
Expression compPropExpr = Expression.Property(downCastExpr, prop);
Expression eqExpr = Expression.Call(null, eqMethod, Expression.Convert(thisPropExpr, typeof(object)), Expression.Convert(compPropExpr, typeof(object)));
return Expression.And(expr, eqExpr);
});
return Expression.Lambda<Func<BaseObject, bool>>(propCompExpr, paramExpr);
}
You can then use it like this:
public class SubObject : BaseObject
{
public int Id { get; set; }
public string Name { get; set; }
private Func<BaseObject, bool> eqFunc;
public bool IsEqualTo(SubObject other)
{
if(this.eqFunc == null)
{
var compExpr = this.CreatePropertiesEqualExpression(other);
this.eqFunc = compExpr.Compile();
}
return this.eqFunc(other);
}
}

Categories

Resources