【问题标题】:How to run dynamic query containing subquery with EF Core?如何使用 EF Core 运行包含子查询的动态查询?
【发布时间】:2022-09-13 23:41:40
【问题描述】:

我能够使用 EF Core 运行各种动态组合的查询除非包含子查询.

因此,这不是 EF Core dynamic lambda subquery not working 或我能找到的任何其他内容的重复。

可以在 dotnetfiddle.net/4opEqr 上找到演示该问题的可运行重现,它使用表示以下查询的动态组合表达式:

efContext.Products.Where(p => p.Id == efContext.OrderItem.Max(i => i.ProductId)).ToList();

我得到的例外是

System.InvalidOperationException : The LINQ expression 'InternalDbSet<OrderItem> {  }
    .Max(i => i.ProductId)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

我观察到相同的行为Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.InMemory(版本 6.0.8 和 7.0.0-preview.7.22376.2)。

【问题讨论】:

  • 你想建立什么? DistinctBy?
  • 这只是演示似乎适用于任何子查询的问题的简短示例。
  • 背景信息:这是我提出问题的动机github.com/6bee/Remote.Linq/issues/112
  • 看起来您正在尝试解决 Remote.Linq 序列化的问题?
  • @IvanStoev,很高兴知道您可以通过网络执行 LINQ 查询。我很难想象实际的用法,但有时它是需要的。 2 年前,当 OP 试图通过 TCP/IP 执行 EF 生成的 SQL 并返回结果时,回答了类似的问题。

标签: entity-framework-core subquery


【解决方案1】:

问题似乎是 EF Core 处理表达式树中嵌套的可查询表达式的方式,以及您尝试测试您创建的动态表达式的方式。

不久,这里

productQueryable.Where(p => p.Id == orderItemQueryable.Max(i => i.ProductId))

productQueryable 是常规变量(.Where 是常规调用),orderItemQueryable 是编译器生成的闭包(作为可查询的Where 谓词表达式的一部分),而在这里

() => productQueryable.Where(p => p.Id == orderItemQueryable.Max(i => i.ProductId))

它们都是编译器生成的闭包。

因此,EF Core 无法正确处理在“根”查询表达式中保存嵌套可查询变量值的 Constant 表达式,因此对于子查询,您应该直接传递 IQueryable.Expression 属性值,或者模拟闭包(但不是常量)表达式。而对于 root 可查询,您应该将其包装在常量表达式中,或者在模拟闭包中(但不是直接)。

由于包装可查询变量适用于这两种情况,因此解决方案是始终将此类变量包装在闭包模拟表达式中。闭包模拟表达式是任何包含常量表达式的表达式,其中包含一些类实例,其中包含类属性或字段中的实际变量。

它可以通过多种方式实现,例如使用 System.Tuple 类作为持有者:

static Expression MakeClosure<T>(T value)
{
    var closure = new Tuple<T>(value);
    return Expression.Property(Expression.Constant(closure), nameof(closure.Item1));
}

或真正的编译器生成的闭包类实例:

static Expression MakeClosure<T>(T value)
{
    var closure = new { value };
    return Expression.Property(Expression.Constant(closure), nameof(closure.value));
}

或使用编译器生成的包含闭包的 lambda 表达式的主体:

static Expression MakeClosure<T>(T value)
{
    Expression<Func<T>> lambda = () => value;
    return lambda.Body;
}

最后,在所有情况下,将示例代码更改为

var productQueryableExp = MakeClosure(productQueryable);
var orderItemQueryableExp = MakeClosure(orderItemQueryable);

一切都会按预期进行。至少使用 EF Core。您似乎正在使用的另一个库(Remote.Linq)呢,不知道(我想这将是另一个问题)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    相关资源
    最近更新 更多