How to add an AND/OR expression to the following dynamic linq expression

.net c# dynamic-linq expression-trees linq

Question

T is a type that may or may not have a specific property, lets say 'City'. For the types that have a property named 'City', I would like to restrict the records such that only residents of the Gotham are returned and they are sorted.

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) 
{

    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = Expression.Call(
                typeof(Queryable), 
                "OrderBy", 
                new     Type[] { type, property.PropertyType }, 
                source.Expression, 
                Expression.Quote(orderByExp));


    string propertyToRestrictOn = "City";
    string restrictedValue = "Gotham";
    var restrictedProperty = type.GetProperty(propertyToRestrictOn);
    if(null ! = restrictedProperty )
    {
      // TODO: What to add here so than only those records are returned that have a 
      // property named City and the value is 'Gotham'???
    }

   return source.Provider.CreateQuery<T>(resultExp);
}

if possible please name/link some helpful literature here as well just in case I have to create more complex queries i.e. mix And/OR

The code was borrowed from How do I apply OrderBy on an IQueryable using a string column name within a generic extension method?

1
0
5/23/2017 11:56:01 AM

Accepted Answer

I think you're making this harder than you have to. In the first part of your code (the OrderBy()), you don't actually need to generate the whole query expression, just the lambda inside it. And in the second part (the optional Where()) you can do pretty much the same thing, just add Expression.Equal() and Expression.Constant():

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering)
{
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    // necessary for value types to work
    var cast = Expression.Convert(propertyAccess, typeof(object));
    var orderByExp = Expression.Lambda<Func<T, object>>(cast, parameter);

    IQueryable<T> result = source.OrderBy(orderByExp);

    string propertyToRestrictOn = "City";
    string restrictedValue = "Gotham";
    var restrictedProperty = type.GetProperty(propertyToRestrictOn);
    if (restrictedProperty != null)
    {
        var restrictionParameter = Expression.Parameter(type, "p");
        var restrictionPropertyAccess =
            Expression.MakeMemberAccess(restrictionParameter, restrictedProperty);
        var restrictionEquality =
            Expression.Equal(restrictionPropertyAccess,
                             Expression.Constant(restrictedValue));
        var whereExp =
            Expression.Lambda<Func<T, bool>>(restrictionEquality, restrictionParameter);

        result = result.Where(whereExp);
    }

   return result;
}

Also, if your method is going to do more than just ordering, I think you shouldn't call it OrderBy().

1
1/24/2013 7:30:36 PM

Popular Answer

I'm not quite sure if I have understood you correctly, but I think I was in the same situation a few months ago. The posted code here was my solution.

I think you should be especially interested in line 24.


Edit:

PropertyInfo p = ... // I used reflection in my examply to get properties with a certain Attribute

var expressionParameter = Expression.Parameter(typeof(SomeClass), "lambda");    
var parameter = new [] { expressionParameter };

var propertyAccess = Expression.Property(expressionParameter, p);
var nullCheck = Expression.NotEqual(propertyAccess, Expression.Constant(null, p.PropertyType));
var nullCheckLambda = Expression.Lambda<Func<SomeClass, Boolean>>(nullCheck, parameter);

var containsMethodInfo = typeof(String).GetMethod("Contains", new[] { typeof(String) });
var contains = Expression.Call(propertyAccess, containsMethodInfo, Expression.Constant("ell"));
var containsLambda = Expression.Lambda<Func<SomeClass, Boolean>>(contains, new[] { expressionParameter });

var predicate = Expression.Lambda<Func<SomeClass, Boolean>>(
// line 24 
Expression.AndAlso(nullCheckLambda.Body, containsLambda.Body), parameter);

Console.WriteLine(predicate.ToString());


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow