Search code examples
c#linqlambdalinq-to-entitiesexpression-trees

How to get dynamic predicate using expressions when joining in Linq


I wrote a static method which will give appropriate predicate lambda using expression api for different type of fields.

static Func<T, bool> Process<T>(string type)
{

    ParameterExpression parameter = Expression.Parameter(typeof(T));
    Expression predicate = Expression.Constant(true);
    if (type == "Start")
    {
        Expression prop = Expression.Property(parameter, type);
        Expression filter = Expression.LessThan(prop, Expression.Constant(DateTime.Now));
        predicate = Expression.AndAlso(predicate, filter);
    }
    //TODO: more if statments to come
    var lambda = Expression.Lambda<Func<T, bool>>(predicate, parameter);
    return lambda.Compile();
}

The above code works fine if I do a single query operation in linq like this:

var del = Process<MyClass>("Start");
var data = DbContext.MyClass.Where(del); //gets the result data

public class MyClass
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
    public long Id { get; set; }
}

Now, if I do a join on the MyClass entity

var data = DbContext.MyClass
         .Join(DbContext.Detail, mc => mc.Id, detail => detail.Id, (m, d) =>
                     new { m = m, d = d})
         .Where(del);

The above lines are giving compile time error

Error   CS1929  'IQueryable<<anonymous type: MyClass m, Detail d>>' does not contain a definition for 'Where' and the best extension method overload 'EnumerableRowCollectionExtensions.Where<MyClass>(EnumerableRowCollection<MyClass>, Func<MyClass, bool>)' requires a receiver of type 'EnumerableRowCollection<MyClass>'

I understand that .Where() now expects an anonymous type which has m and d but not sure how to resolve this.

I'm pretty new to Expression API. Not sure how to achieve.

I've tried to achieve that by creating anonymous type variable, but that is a variable and not the type itself to pass it to Process<T>().


Solution

  • The result of your Process method is a Where clause for objects of type MyClass, so you can't use it to filter anonymous objects { m, d }. Instead filter before the Join:

    var data = DbContext.MyClass.Where(del);                        
                                .Join(DbContext.Detail,
                                      mc => mc.Id,
                                      detail => detail.Id,
                                      (m, d) => new { m, d }
                                 );