在另一个表达式中使用保存的表达式
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 执行。
现在您关心的是将数据加载到内存中。
在您实际枚举查询之前,IsAGood
和 IsBGood
以及您的组合查询都不会将任何数据加载到内存中(例如使用 .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);
是否可以这样做:
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 执行。
现在您关心的是将数据加载到内存中。
在您实际枚举查询之前,IsAGood
和 IsBGood
以及您的组合查询都不会将任何数据加载到内存中(例如使用 .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);