0

I'm trying to use a lambda expression as a predicate for a call to IEnumerable.Where via reflection.

e.Query is of type System.Data.Objects.ObjectQuery<T> where T is known at Runtime. ObjectQuery implements IEnumerable.

//the current element type
Type currentType = e.Query.ElementType;

ParameterExpression typeParameterExpression = Expression.Parameter(currentType);
ConstantExpression propertyConstantExpression = Expression.Constant(GameId, GameId.GetType());
BinaryExpression equalityExpression = Expression.Equal(Expression.PropertyOrField(typeParameterExpression, "GameId"), propertyConstantExpression); 

Type genericFunc = typeof(Func<,>).MakeGenericType(currentType, typeof(bool)); //genericFunc = {Name = "Func`2" FullName = "System.Func`2[[OfferManagementBackOffice.Placement, OfferManagementBackOffice, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
Expression predicateExpression = Expression.Lambda(genericFunc, equalityExpression, typeParameterExpression); //predicateExpression = {Param_0 => (Param_0.GameId == 2)}

var whereMethods = typeof(System.Linq.Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Where");
MethodInfo whereMethod = null;
foreach (var methodInfo in whereMethods)
{
    var paramType = methodInfo.GetParameters()[1].ParameterType;
    if (paramType.GetGenericArguments().Count() == 2)
    {
        // we are looking for  Func<TSource, bool>, the other has 3
        whereMethod = methodInfo;
    }
}

whereMethod = whereMethod.MakeGenericMethod(currentType);
//whereMethod = {System.Collections.Generic.IEnumerable`1[OfferManagementBackOffice.Placement] Where[Placement](System.Collections.Generic.IEnumerable`1[OfferManagementBackOffice.Placement], System.Func`2[OfferManagementBackOffice.Placement,System.Boolean])}

var result = whereMethod.Invoke(e.Query, new object[] { e.Query, predicateExpression });

When I try the Invoke() on whereMethod I get this error:

Error: Object of type 'System.Linq.Expressions.Expression``1[System.Func``2[OfferManagementBackOffice.Placement,System.Boolean]]' cannot be converted to type 'System.Func``2[OfferManagementBackOffice.Placement,System.Boolean]'.

What I'm trying to achieve above is this (here T = Placement), but for any type.

Expression<Func<Placement, bool>> lambda1 = Expression.Lambda<Func<Placement, bool>>(equalityExpression, typeParameterExpression);
e.Query.Cast<Placement>().Where(lambda1);
D Stanley
  • 149,601
  • 11
  • 178
  • 240
Adrian Buzea
  • 826
  • 2
  • 11
  • 33
  • 1
    you need [`.Compile()`](https://msdn.microsoft.com/en-us/library/Bb345362(v=VS.110).aspx) – Grundy Sep 02 '15 at 12:59
  • are you sure that you need `e.Query.Cast().Where(lambda1);` instead of `e.Query.Where(lambda1);`? – Grundy Sep 02 '15 at 13:01
  • Yes, I can't call Where on e.Query because it's an IQueryable(so an IEnumerable) and you can only call the Linq extension methods on Generic IEnumerable, so I have to cast it first – Adrian Buzea Sep 02 '15 at 13:02
  • .Compile() solved my problem, thank you very much! You can add it as an answer if you want! – Adrian Buzea Sep 02 '15 at 13:05
  • see a bit about [`Queryable.Where`](https://msdn.microsoft.com/en-us/library/system.linq.queryable.where(v=vs.110).aspx) – Grundy Sep 02 '15 at 13:07
  • How would I go about converting result to an IQueryable ? e.Query = (IQueryable)result; throws Unable to cast object of type 'WhereEnumerableIterator`1[OfferManagementBackOffice.Placement]' to type 'System.Linq.IQueryable'. – Adrian Buzea Sep 02 '15 at 13:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88583/discussion-between-grundy-and-adrian). – Grundy Sep 02 '15 at 13:11

1 Answers1

0

First, I would like to thank @Grundy for taking his time to help me find the solution to this:

In order to make the Where call work I had to .Compile() the lambda expression

var predicateExpression = Expression.Lambda(genericFunc, equalityExpression, typeParameterExpression).Compile();

That works but it returns a WhereEnumerableIterator<T> instead of the needed ObjectQuery.

It turns out I needed to use Queryable.Where and not Enumerable.Where. But now, for some strange reason I don't need to(in fact I must not) Compile() the lambda expression for it to work. Here's the final working code:

short GameId = Convert.ToInt16(Session["GlobalGameFilter"]);
Type currentType = e.Query.ElementType;

ParameterExpression typeParameterExpression = Expression.Parameter(currentType);
ConstantExpression propertyConstantExpression = Expression.Constant(GameId, GameId.GetType());
BinaryExpression equalityExpression = Expression.Equal(Expression.PropertyOrField(typeParameterExpression, "GameId"), propertyConstantExpression);

Type genericFunc = typeof(Func<,>).MakeGenericType(currentType, typeof(bool));
var predicateExpression = Expression.Lambda(genericFunc, equalityExpression, typeParameterExpression);

var whereMethods = typeof(System.Linq.Queryable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Where");
MethodInfo whereMethod = whereMethods.ElementAt(0).MakeGenericMethod(currentType);


var result = whereMethod.Invoke(e.Query, new object[] { e.Query, predicateExpression });

e.Query = (IQueryable)result;

And now result is of type ObjectQuery which is exactly what I need.

Adrian Buzea
  • 826
  • 2
  • 11
  • 33
  • `genericFunc` is redundant, `Expression.Lambda(equalityExpression, typeParameterExpression)` call will do the same. you can see this and some other useful tricks here http://stackoverflow.com/questions/32219773/dynamic-linq-query-to-get-field-value-from-database/32321711#32321711 – Ivan Stoev Sep 02 '15 at 23:31