在另一个表达式中使用保存的表达式

Use a saved expression inside another expression

是否可以这样做:

        public static Expression<Func<EntityA, bool>> IsAGood =
            x => x.A != null &&
            x.A.valid;

        public static Expression<Func<EntityB, bool>> IsBGood=
            x => IsAGood (x.EntityA);

        var res = context.EntitiesB
                 .Where(x => x.count > 0)
                 .Where(IsBGood)

我知道我可以在 x.EntityA 上编译 IsAGood 和 运行 它,但它会将数据加载到内存中,我还不想这样做。 有没有办法在不加载到内存的情况下做到这一点?

请注意,您不能在此上下文中对 lambda 表达式调用 Compile,因为这会创建一个委托。但是 EF 需要一个 Expression,因为 o/r-mapper 需要它包含的语法信息才能将其转换为 SQL 命令。可执行委托无法转换为 SQL 且无法由 DB 执行。


现在您关心的是将数据加载到内存中。 在您实际枚举查询之前,IsAGoodIsBGood 以及您的组合查询都不会将任何数据加载到内存中(例如使用 .ToList()foreach)。

您甚至可以动态添加部件,如下所示:

var query = context.EntitiesB
    .Where(x => x.count > 0); // No data access happens here.
if (useCondition) {
    query = query.Where(IsBGood); // No data access happens here.
}
var result = query.ToList(); // The DB will be accessed here once.

请注意,您的代码的所有部分都只设置了一个查询。他们不执行任何查询。因此我将 var 的名称从 res 更改为 query.


好的,我明白了。 IsAGood 不能这样调用,因为它是一个 Expression<>,而不是一个委托(正如我之前解释的那样,委托无论如何都不起作用)。

您可以按照@GuruStron 的解释重写表达式或将测试更改为

来解决此问题
public static Expression<Func<EntityB, bool>> IsAinBGood =
        b => b.EntityA.A != null &&
             b.EntityA.A.valid;

是的,您可以通过一些 expression trees 处理来完成。也许有更简单的方法来做到这一点,但我知道的看起来像这样(你可以将这段代码放在静态构造函数中):

Expression<Func<EntityB, EntityA>> exp = b => b.EntityA;
var param = exp.Parameters.First();
var expression = new ReplacingExpressionVisitor(new[]{IsAGood.Parameters.First()}, new []{exp.Body}).Visit(IsAGood.Body);
IsBGood = Expression.Lambda<Func<EntityB, bool>>(expression, param);

ReplacingExpressionVisitor 从 EF Core 3.0 开始可用,如果您使用的是旧版本,您可以自己编写一个,应该不会那么难。

您也可以尝试使用 Expression.Inkove(但我认为我之前在翻译它时遇到了一些问题):

Expression<Func<EntityB, EntityA>> exp = b => b.EntityA;
var param = exp.Parameters.First();
IsBGood = Expression.Lambda<Func<EntityB, bool>>(Expression.Invoke(IsAGood, exp.Body), param);