手动创建Lambda表达式以用作谓词的位置

时间:2015-09-02 12:57:10

标签: c# linq reflection lambda

我尝试使用lambda表达式作为通过反射调用IEnumerable.Where的谓词。

e.Query的类型为System.Data.Objects.ObjectQuery<T>,其中T在运行时已知。 ObjectQuery实现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 });

当我在whereMethod上尝试Invoke()时,我收到此错误:

  

错误:类型的对象   'System.Linq.Expressions.Expression``1[System.Func``2[OfferManagementBackOffice.Placement,System.Boolean]]'   无法转换为类型   'System.Func``2[OfferManagementBackOffice.Placement,System.Boolean]'

我在上面尝试实现的目标是(此处为T = Placement),但适用于任何类型。

Expression<Func<Placement, bool>> lambda1 = Expression.Lambda<Func<Placement, bool>>(equalityExpression, typeParameterExpression);
e.Query.Cast<Placement>().Where(lambda1);

1 个答案:

答案 0 :(得分:0)

首先,我要感谢@Grundy花时间帮助我找到解决方案:

为了使Where调用工作,我必须.Compile()lambda表达式

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

可行,但它返回WhereEnumerableIterator<T>而不是所需的ObjectQuery。

事实证明我需要使用Queryable.Where而不是Enumerable.Where。但是现在,由于一些奇怪的原因,我不需要(实际上我不能)Compile()lambda表达式使它工作。这是最终的工作代码:

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;

现在结果是ObjectQuery类型,这正是我需要的。