Dynamically Build Linq Lambda Expression - c#

Okay, my guess is this is answered somewhere already, and I'm just not quite familiar enough with the syntax yet to understand, so bear with me.
The users of my web app need to filter a long list of items in a gridview, accessed via a linqdatasource. I am using the OnSelecting Event to further filter items. I want to filter those items based on selections the users make in DropDownLists.
For example, they select "Title" "Contains" "Fred"
This results in
e.Result = dbContext.Opps.Where(opp => opp.Title.Contains("Fred"));
Or "Description" "Does not Contain" "Alpha"
results in
e.Result = dbContext.Opps.Where(opp => !opp.Description.Contains("Alpha"));
I would like to build up that Expression (System.Linq.Expressions.Expression>) dynamically, instead of having nested switch expressions to generate it, as there are a number of fields I want to check, and I also want to use the StartsWith and EndsWith checks. If I could build the Expression as a string, like so:
string stringExpression = string.Format("opp => opp.{0}.{1}(\"{2}\")",
ddlCustomFilter.SelectedValue,
ddlFilterType.SelectedValue,
txtFilterText.Text);
And then somehow have it be converted into an Expression... is this possible? Or should I just bite the bullet and generate all the switch() statements required to create the various expressions?

You could certainly build the expression dynamically, but I would consider using Dynamic LINQ as an alternative first, though you may not be able to use Contains. In addition, you may want to consider using PredicateBuilder to build complex queries additively.

try this code...
call ToExpression Method()....
public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
{
Expression<Func<T, bool>> func = null;
try
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var arrProp = propName.Split('.').ToList();
Expression binExpr = null;
string partName = string.Empty;
arrProp.ForEach(x =>
{
Expression tempExpr = null;
partName = partName.IsNull() ? x : partName + "." + x;
if (partName == propName)
{
var member = NestedExprProp(paramExpr, partName);
var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type;
tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type));
}
else
tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null));
if (binExpr != null)
binExpr = Expression.AndAlso(binExpr, tempExpr);
else
binExpr = tempExpr;
});
Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr);
if (expr != null)
innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr);
func = innerExpr;
}
catch { }
return func;
}
private static MemberExpression NestedExprProp(Expression expr, string propName)
{
string[] arrProp = propName.Split('.');
int arrPropCount = arrProp.Length;
return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName);
}
private static Expression ToExprConstant(Type prop, string value)
{
if (value.IsNull())
return Expression.Constant(value);
object val = null;
switch (prop.FullName)
{
case "System.Guid":
val = value.ToGuid();
break;
default:
val = Convert.ChangeType(value, Type.GetType(prop.FullName));
break;
}
return Expression.Constant(val);
}
private static Expression ApplyFilter(string opr, Expression left, Expression right)
{
Expression InnerLambda = null;
switch (opr)
{
case "==":
case "=":
InnerLambda = Expression.Equal(left, right);
break;
case "<":
InnerLambda = Expression.LessThan(left, right);
break;
case ">":
InnerLambda = Expression.GreaterThan(left, right);
break;
case ">=":
InnerLambda = Expression.GreaterThanOrEqual(left, right);
break;
case "<=":
InnerLambda = Expression.LessThanOrEqual(left, right);
break;
case "!=":
InnerLambda = Expression.NotEqual(left, right);
break;
case "&&":
InnerLambda = Expression.And(left, right);
break;
case "||":
InnerLambda = Expression.Or(left, right);
break;
case "LIKE":
InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
break;
case "NOTLIKE":
InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right));
break;
}
return InnerLambda;
}
public static Expression<Func<T, object>> PropExpr<T>(string PropName)
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var tempExpr = Extentions.NestedExprProp(paramExpr, PropName);
return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr);
}
public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord)
{
return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc;
}
public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

Related

How to append expressions in linq?

Net core application. I have a generic repository pattern implemented. I am trying to implement some filtering functionality. I have the below code.
var param = Expression.Parameter(typeof(SiteAssessmentRequest), "x");
Expression<Func<SiteAssessmentRequest, bool>> query;
query = x => x.CreatedBy == request.Userid || x.AssignedTo == request.Userid;
Expression body = Expression.Invoke(query, param);
if (request.Client != null && request.Client.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.Client.Contains(x.Client);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.CountryId != null && request.CountryId.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.CountryId.Contains(x.CountryId);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.SiteName != null && request.SiteName.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.SiteName.Contains(x.SiteName);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
if (request.Status != null && request.Status.Length != 0)
{
Expression<Func<SiteAssessmentRequest, bool>> internalQuery = x => request.Status.Contains(x.Status);
body = Expression.AndAlso(Expression.Invoke(query, param), Expression.Invoke(internalQuery, param));
}
var lambda = Expression.Lambda<Func<SiteAssessmentRequest, bool>>(body, param);
var siteAssessmentRequest = await _siteAssessmentRequestRepository.GetAsync(lambda, null, x => x.Country).ConfigureAwait(false);
In the above code when I pass more than one parameter, for example, request. Status and request.SiteName I want to filter based on status and Sitename. When I see the query only one parameter appended in the query
{x => (Invoke(x => (Not(IsNullOrEmpty(x.CreatedBy)) AndAlso Not(IsNullOrWhiteSpace(x.CreatedBy))), x)
AndAlso Invoke(x => value(Site.V1.Implementation.GetSARByFilterAr+<>c__DisplayClass12_0)
.request.Status.Contains(x.Status), x))}
After searching so much I got below code
public static class ExpressionCombiner
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> exp, Expression<Func<T, bool>> newExp)
{
// get the visitor
var visitor = new ParameterUpdateVisitor(newExp.Parameters.First(), exp.Parameters.First());
// replace the parameter in the expression just created
newExp = visitor.Visit(newExp) as Expression<Func<T, bool>>;
// now you can and together the two expressions
var binExp = Expression.And(exp.Body, newExp.Body);
// and return a new lambda, that will do what you want. NOTE that the binExp has reference only to te newExp.Parameters[0] (there is only 1) parameter, and no other
return Expression.Lambda<Func<T, bool>>(binExp, newExp.Parameters);
}
class ParameterUpdateVisitor : ExpressionVisitor
{
private ParameterExpression _oldParameter;
private ParameterExpression _newParameter;
public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(node, _oldParameter))
return _newParameter;
return base.VisitParameter(node);
}
}
}
But I am struggling to make it to work above code.
In the above query, I see only status but not site name. So I want to include multiple expressions. can someone help me regarding this? Any help would be greatly appreciated. Thanks
Let me explain core concept of LambdaExpression. LambdaExpression has 0..N parameters and Body. Body of LambdaExpression is Expression which actually we want to reuse.
Basic mistake is trying to combine lambda expressions (changed parameter names because it is how ExpressionTree works - it compares parameters by reference, not by name):
Expression<Func<Some, bool>> lambda1 = x1 => x1.Id == 10;
Expression<Func<Some, bool>> lambda2 = x2 => x2.Value == 20;
// wrong
var resultExpression = Expression.AndAlso(lambda1, lambda2);
Schematically previous wrong sample should looks like (even it will crash)
(x1 => x1.Id == 10) && (x2 => x2.Value == 20)
What we have - not desired expression but combination of "functions".
So let reuse body of LambdaExpression
// not complete
var newBody = Expression.AndAlso(lambda1.Body, lambda2.Body);
Result is more acceptable but still needs corrections:
(x1.Id == 10) && (x2.Value == 20)
Why we need correction? Because we are trying to build the following LambdaExpression
var param = Expression.Parameter(typeof(some), "e");
var newBody = Expression.AndAlso(lambda1.Body, lambda2.Body);
// still wrong
var newPredicate = Expression.Lambda<Func<Some, bool>>(newBody, param)
Result of newPredicate should looks like
e => (x1.Id == 10) && (x2.Value == 20)
As you can see we left in body parameters from previous two lambdas, which is wrong and we have to replace them with new parameter param ("e") and better to do that before combination.
I'm using my implementation of replacer, which just returns desired body.
So, let's write correct lambda!
Expression<Func<Some, bool>> lambda1 = x1 => x1.Id == 10;
Expression<Func<Some, bool>> lambda2 = x2 => x2.Value == 20;
var param = Expression.Parameter(typeof(Some), "e");
var newBody = Expression.AndAlso(
ExpressionReplacer.GetBody(lambda1, param),
ExpressionReplacer.GetBody(lambda2, param));
// hurray!
var newPredicate = Expression.Lambda<Func<Some, bool>>(newBody, param);
var query = query.Where(newPredicate);
After theses steps you will have desired result:
e => (e.Id == 10) && (e.Value == 20)
And ExpressionReplacer implementation
class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Zip(lambda.Parameters, toReplace, (f, s) => Tuple.Create(f, s))
.ToDictionary(e => (Expression)e.Item1, e => e.Item2)).Visit(lambda.Body);
}
}
If you plan to work with ExpressionTree closer, I suggest to install this VS extension, which should simplify your life: Readable Expressions

C# Using dynamic Linq expressions filter a column that is of type int using Startswith and getting Nullable`1 for conversion

As a basis for filtering using dynamic linq I used this answer and it works for columns of type String however I also needed to do startsWith/Contains on integer type columns as well - in this case I used this answer - and it falls over with the error shown a little below.
Here is the code that creates the IQueryable result.
public static IQueryable<T> Has<T>(this IQueryable<T> source, ExpressionFilter filter) {
if (source == null || filter.PropertyName.IsNull() || filter.Value == null) {
return source;
}
// Find the type incase we get an int column.
Type propertyType = source.ElementType.GetProperty(filter.PropertyName).PropertyType;
// For our Call.
MethodCallExpression methodCallExpression = null;
// If its a string we need to change it to lower case.
MethodCallExpression toLowerExpression = null;
// If its any one of the binary expressions.
Expression binaryExpression = null;
// ..and our predicate is initiated here as well.
Expression<Func<T, bool>> predicate = null;
// We need the parameter eg x => ..
ParameterExpression parameter = Expression.Parameter(source.ElementType, "x");
// Finally here is our Property expression eg x => LastName Last name being the property name
Expression propertyExp = Expression.Property(parameter, filter.PropertyName);
// our METHODINFO's
var CONTAINS_METHOD = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var STARTS_WITH_METHOD = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var ENDS_WITH_METHOD = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var BOOL_EQUAL_METHOD = typeof(bool).GetMethod("Equals", new Type[] { typeof(bool) });
var TO_LOWER_METHOD = typeof(string).GetMethod("ToLower", new Type[] { });
var TRIM_START = typeof(string).GetMethod("Trim", new Type[] { typeof(int) });
var STRING_CONVERT = typeof(SqlFunctions).GetMethod("StringConvert", new[] { typeof(double?) }); ////get the SqlFunctions.StringConvert method for nullable double
// We supply a type of object for our term to search on.. it needs to be a string.
ConstantExpression termConstant = Expression.Constant(filter.Value.ToString(), typeof(string));
// In case we get a propertyType of (int) we cant just perform a lower case on it.
if (propertyType == typeof(string)) {
toLowerExpression = Expression.Call(propertyExp, TO_LOWER_METHOD);
}
switch (filter.Comparison) {
case Comparison.Contains:
methodCallExpression = Expression.Call(toLowerExpression, CONTAINS_METHOD, termConstant);
break;
case Comparison.StartsWith:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
else
methodCallExpression = Expression.Call(toLowerExpression, STARTS_WITH_METHOD, termConstant); //WORKS HERE..
break;
case Comparison.EndsWith:
methodCallExpression = Expression.Call(toLowerExpression, ENDS_WITH_METHOD, termConstant);
break;
case Comparison.BoolTest:
bool parsedBoolValue;
if (bool.TryParse(filter.Value.ToString().ToLower(), out parsedBoolValue)) { // Its a bool column.
termConstant = Expression.Constant(parsedBoolValue, typeof(bool));
methodCallExpression = Expression.Call(propertyExp, BOOL_EQUAL_METHOD, termConstant);
}
break;
case Comparison.Equal:
binaryExpression = Expression.Equal(propertyExp, termConstant);
break;
case Comparison.GreaterThan:
binaryExpression = Expression.GreaterThan(propertyExp, termConstant);
break;
case Comparison.GreaterThanOrEqual:
binaryExpression = Expression.GreaterThanOrEqual(propertyExp, termConstant);
break;
case Comparison.LessThan:
binaryExpression = Expression.LessThan(propertyExp, termConstant);
break;
case Comparison.LessThanOrEqual:
binaryExpression = Expression.LessThanOrEqual(propertyExp, termConstant);
break;
case Comparison.NotEqual:
binaryExpression = Expression.NotEqual(propertyExp, termConstant);
break;
case Comparison.IndexOf:
binaryExpression = Expression.NotEqual(
Expression.Call(
propertyExp,
"IndexOf",
null,
Expression.Constant(filter.Value, typeof(string)),
Expression.Constant(StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison))
),
Expression.Constant(-1, typeof(int))
);
break;
default:
return null;
}
if (binaryExpression == null) {
predicate = Expression.Lambda<Func<T, bool>>(methodCallExpression, parameter);
}
else {
predicate = Expression.Lambda<Func<T, bool>>(binaryExpression, parameter);
}
methodCallExpression = Expression.Call(typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery<T>(methodCallExpression);
}
So it works with columns of string type but when you try and do a filter on an int column it fails with..
"Operation is not valid due to the current state of the object."
For an int column it builds the expression with:
if (propertyType == typeof(int)) { // We'll do a startsWith on an int column
//convert Expression to a nullable double (or nullable decimal),
//so that you can use SqlFunctions.StringConvert
propertyExp = Expression.Convert(propertyExp, typeof(double?));
//call StringConvert on your converted expression
propertyExp = Expression.Call(null, STRING_CONVERT, propertyExp);
methodCallExpression = Expression.Call(propertyExp, STARTS_WITH_METHOD, termConstant);
}
and as an expression this yields
{x => StringConvert(Convert(x.ClientNo,
Nullable`1)).StartsWith("101")}
I could be wrong here but why would it put a back tick and a "1" after nullable?? is this why it falls over?
For completeness at the point it completes the "Has" method (adds to the expression tree) and this is the result (which fails):
Is there a better way to construct this expression so it works?

Trying to create a morethan, equal or greaterthan dynamic filter for dates in linq

I have been trying to create a expression tree filter for Linq that take 2 dates and a string of possible values { "lessthan", "equals", "morethan" }. I wish to format the call to be something like Query.Where(CompareDates(x => x.left, right, "less than"));
I have the code:
public static IQueryable<TSource> CompareDates<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, DateTime?>> left,
DateTime? right,
string equality)
{
if (right == null || string.IsNullOrWhiteSpace(equality))
return source;
var p = left.Parameters.Single();
Expression member = p;
Expression leftExpression = Expression.Property(member, "left");
Expression rightParameter = Expression.Constant(right, typeof(DateTime));
BinaryExpression BExpression = null;
switch (equality)
{
case "lessthan":
BExpression = Expression.LessThan(leftExpression, rightParameter);
break;
case "equal":
BExpression = Expression.Equal(leftExpression, rightParameter);
break;
case "morethan":
BExpression = Expression.GreaterThan(leftExpression, rightParameter);
break;
default:
throw new Exception(String.Format("Equality {0} not recognised.", equality));
}
return source.Where(Expression.Lambda<Func<TSource, bool>>(BExpression, p));
}
Unfortunately it is producing an error of "System.ArgumentException: Instance property 'left' is not defined for type 'Model' at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName) at SARRestAPI.Extensions.Expressions.CompareDates[TSource](IQueryable1 source, Expression1 src, DateTime supplied, String equality)"
Anyone have an ideas why this is happening?
Here we go; what you want to do is use the .Body of the incoming selector, not look for .left. Meaning, given an input selector of x => x.Foo.Bar.Blap, a constant, and a comparison, you want to construct something like x => x.Foo.Bar.Blap < someValue, by reusing both the x (the parameter, which you're already doing) and the body (x.Foo.Bar.Blap).
In code (note this works for any TValue, not just DateTime):
public enum Comparison
{
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual
}
public static IQueryable<TSource> Compare<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource, TValue>> selector,
TValue value,
Comparison comparison)
{
Expression left = selector.Body;
Expression right = Expression.Constant(value, typeof(TValue));
BinaryExpression body;
switch (comparison)
{
case Comparison.LessThan:
body = Expression.LessThan(left, right);
break;
case Comparison.LessThanOrEqual:
body = Expression.LessThanOrEqual(left, right);
break;
case Comparison.Equal:
body = Expression.Equal(left, right);
break;
case Comparison.NotEqual:
body = Expression.NotEqual(left, right);
break;
case Comparison.GreaterThan:
body = Expression.GreaterThan(left, right);
break;
case Comparison.GreaterThanOrEqual:
body = Expression.GreaterThanOrEqual(left, right);
break;
default:
throw new ArgumentOutOfRangeException(nameof(comparison));
}
return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));
}
Example usage (here using LINQ-to-Objects, but it should work for other LINQ backends, too):
var arr = new[] { new { X = 11 }, new { X = 12 }, new { X = 13 }, new { X = 14 } };
var source = arr.AsQueryable();
var filtered = source.Compare(x => x.X, 12, Comparison.GreaterThan);
foreach (var item in filtered)
{
Console.WriteLine(item.X); // 13 and 14
}
Note that with C# vCurrent you can do:
var body = comparison switch
{
Comparison.LessThan => Expression.LessThan(left, right),
Comparison.LessThanOrEqual => Expression.LessThanOrEqual(left, right),
Comparison.Equal => Expression.Equal(left, right),
Comparison.NotEqual => Expression.NotEqual(left, right),
Comparison.GreaterThan => Expression.GreaterThan(left, right),
Comparison.GreaterThanOrEqual => Expression.GreaterThanOrEqual(left, right),
_ => throw new ArgumentOutOfRangeException(nameof(comparison)),
};
return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));
which you might fine preferable.

C# Expressions - Creating an Expression from another Expression

I am trying to create a reusable method using expressions that looks something like this:
Expression<Func<Order, bool>> CreateExpression(Expression<Func<Order, int>> parameter, FilterOperator operator, int value)
So I can use it like this:
IQueryable<Order> orders = db.Orders;
var filtered = orders.Where(CreateExpression(o => o.OrderID, FilterOperator.GreaterThan, 100));
I'm not sure how to write the method though. How can I write a method that will create this Expression for me?
I need to be able to do something like this:
if(operator == FilterOperator.GreaterThan)
return m => m.OrderID > value;
else if(operator == FilterOperator.LessThan)
return m => m.OrderID < value;
But I want to use the expression that is passed in instead of using OrderID directly. How can I do this?
static Expression<Func<T, bool>> CreateExpression<T>(Expression<Func<T, int>> parameter, FilterOperator #operator, int value)
{
var argExpr = Expression.Parameter(typeof(T), "p");
var paramExpr = Expression.Invoke(parameter, argExpr);
var constExpr = Expression.Constant(value);
Expression compExpr = null;
switch(#operator)
{
case FilterOperator.GreaterThan:
compExpr = Expression.GreaterThan(paramExpr, constExpr);
break;
case FilterOperator.LessThan:
compExpr = Expression.LessThan(paramExpr, constExpr);
break;
}
return Expression.Lambda<Func<T, bool>>(compExpr, argExpr);
}
If you can't use Invoke and your parameter expression is a member expression, then you can just re-create it using your new parameter:
static Expression<Func<T, bool>> CreateExpression<T>(Expression<Func<T, int>> parameter, FilterOperator #operator, int value)
{
var memberExpr = (MemberExpression)parameter.Body;
PropertyInfo property = (PropertyInfo)memberExpr.Member;
var argExpr = Expression.Parameter(typeof(T), "p");
var propertyExpr = Expression.Property(argExpr, property);
var constExpr = Expression.Constant(value);
Expression compExpr = null;
switch(#operator)
{
case FilterOperator.GreaterThan:
compExpr = Expression.GreaterThan(propertyExpr, constExpr);
break;
case FilterOperator.LessThan:
compExpr = Expression.LessThan(propertyExpr, constExpr);
break;
}
return Expression.Lambda<Func<T, bool>>(compExpr, argExpr);
}

Compare with string values using IQueryable<T> and Expressions

Sorry about the title but I can't really think of a quick way of saying what I want - could someone please change it to a more appropriate one if you think of one?
I'm trying to turn the following function call into an expression query.
List.Compare("propretyName", ">1000");
public static IQueryable<T> Compare<T>(this IQueryable<T> source, string propertyName, string value) {
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "param");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, type.GetProperty(propertyName));
string comparisonType = value.Substring(0, 1);
value = value.Length > 0 ? value.Substring(1) : "0";
decimal tmpvalue;
decimal? result = decimal.TryParse(value, out tmpvalue) ? tmpvalue : (decimal?)null;
ConstantExpression constant = Expression.Constant(result, typeof(decimal?));
BinaryExpression comparisonExpression = Expression.GreaterThan(memberAccess, constant);
switch (comparisonType) {
case ">":
comparisonExpression = Expression.GreaterThan(memberAccess, constant);
break;
case "<":
comparisonExpression = Expression.LessThan(memberAccess, constant);
break;
case "=":
comparisonExpression = Expression.Equal(memberAccess, constant);
break;
}
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(comparisonExpression, parameter);
return source.Where(lambda);
}
The above is the method I wrote to make that call.
The lambda at the bottom appears to be correct: {param => (param.propretyName > 1000)}
However, it's not working and I think it's because the particular proprety I'm working on is a decimal? so it should be {param => (param.propertyName.Value > 1000)}.
Could anybody help me out to use the Value rather than. There's just something that's escaping me here.
I can't use the Where(string) method as I'm using Entity-Framework.
Answer found
public static IQueryable<T> Compare<T>(this IQueryable<T> source, string propertyName, string value) {
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "param");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, type.GetProperty(propertyName));
//This is the added methods that results in the proper output
PropertyInfo valProp = typeof(Nullable<decimal>).GetProperty("Value");
memberAccess = Expression.MakeMemberAccess(memberAccess, valProp);
string comparisonType = value.Substring(0, 1);
value = value.Length > 0 ? value.Substring(1) : "0";
decimal tmpvalue;
decimal? result = decimal.TryParse(value, out tmpvalue) ? tmpvalue : (decimal?)null;
ConstantExpression constant = Expression.Constant(tmpvalue);
BinaryExpression comparisonExpression = Expression.GreaterThan(memberAccess, constant);
switch (comparisonType) {
case ">":
comparisonExpression = Expression.GreaterThan(memberAccess, constant);
break;
case "<":
comparisonExpression = Expression.LessThan(memberAccess, constant);
break;
case "=":
comparisonExpression = Expression.Equal(memberAccess, constant);
break;
}
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(comparisonExpression, parameter);
return source.Where(lambda);
}
Why not just use the existing Dynamic LINQ library:
myList.Where("propertyName > 1000");

Categories