使用 Func<,> 构建 LambdaExpression,其中 returnType 是 IQueryable<T>

Build LambdaExpression with Func<,> where returnType is IQueryable<T>

我正在尝试创建一种通用方法来根据对象自身的 ID 获取 EntityFramework 对象,而无需将 lambda 表达式作为参数传递给方法 GetById()。对于下面的代码,实体 T 的类型为 Message,为 class 所知,其中 GetById() 已实现,并且具有属性 MessageId 以及其他几个特性。 MessageId 名称已在下面的示例中进行了硬编码,因为这仍处于实验阶段 - 从 T 中提取 id 属性名称稍后很容易修复。

我一直在努力寻找一种方法来构建一个简单的 LambdaExpression,它以 IQueryable<T> 作为参数类型,希望有人能提供有关如何完成此操作的线索。我想要 IQueryable<T> 的原因是因为我的底层通道工厂提供程序需要它来进行更复杂的查询。

下面代码中带有 var exp = Expression.Lambda<...> 的行显示了我想要结束的表达式函数类型定义,但是该行给出了异常:

Expression of type System.Boolean cannot be used for return type IQueryable

那是因为 body 的类型是 Boolean,而我的表达式参数 queryParamtRet 的类型是 IQueryable<Message>。此外,如果我将正文类型更改为 IQueryable<Message>,我将无法找到属性 MessageId,因为类型不再是 T 类型 Message,但是输入 IQueryable<T>.

public T GetById(int id)
{
    var queryParamLeft = Expression
        .Parameter(typeof(System.Data.Entity.DbSet<T>), "o");
    var queryParamRet = Expression
        .Parameter(typeof(IQueryable<T>), "o");
    var entityFrameworkType = Expression
        .Parameter(typeof(T), "o");
    var queryProperty = Expression
        .PropertyOrField(entityFrameworkType, "MessageId");
    var body = Expression
        .Equal(queryProperty, Expression.Constant(id));
    var exp = Expression
        .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
            body,
            queryParamRet);

    var returnXml = DoWithChannel(channel
                        => channel.Load(serializer.Serialize(exp)));
}

TLDR:写出您要为其创建表达式的代码,然后有意地创建表达式,从任何内部表达式开始,然后再将它们组合到外部表达式中。


如果您将预期的代码编写为一个函数,它看起来像这样

public static IQueryable<T> FilterADbSet(DbSet<T> dbSet)
{
    return Queryable.Where<T>(dbSet, o => o.MessageId == 34);
}

它有一个 DbSet<T> 类型的输入参数,一个 IQueryable<T> 类型的输出,它使用 dbSet 变量和一个表达式的参数调用 Queryable.Where<T>

从外到内,您首先需要构建要传递给 where 子句的表达式。你已经在你的代码中做到了。

接下来您需要为 where 子句创建一个 lambda 表达式。

var whereClause = Expression.Equal(queryProperty, Expression.Constant(id));

var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType);

接下来,如注释所示,您需要使用Expression.Call创建一个body。

我使您的代码正常工作的最终结果如下。

static Expression<Func<IQueryable<T>, IQueryable<T>>> WhereMethodExpression = v => v.Where(z => true);
static MethodInfo WhereMethod = ((MethodCallExpression)WhereMethodExpression.Body).Method;

public T GetById(int id)
{
    var queryParamLeft = Expression
        .Parameter(typeof(System.Data.Entity.DbSet<T>), "dbSet");

    var entityFrameworkType = Expression
        .Parameter(typeof(T), "entity");

    var queryProperty = Expression
        .PropertyOrField(entityFrameworkType, "MessageId");

    var whereClause = Expression
        .Equal(queryProperty, Expression.Constant(id));

    var whereClauseLambda = Expression.Lambda<Func<T, bool>>(whereClause, entityFrameworkType);

    var body = Expression.Call(
        WhereMethod,
        queryParamLeft,
        whereClauseLambda
        );

    var exp = Expression
        .Lambda<Func<System.Data.Entity.DbSet<T>, IQueryable<T>>>(
            body,
            queryParamLeft);

    var returnXml = DoWithChannel(channel
                        => channel.Load(serializer.Serialize(exp)));

}
  1. 我使用了表达式来获取 Queryable.Where<T>
  2. 的 MethodInfo object
  3. 您的 body 表达式需要 queryParamLeft 传入。不需要 queryParamRet