【问题标题】:Cannot call Method in LINQ Expression无法在 LINQ 表达式中调用方法
【发布时间】:2022-11-02 20:50:25
【问题描述】:

我正在使用 EFCore 在 C# 中编写应用程序。

我有实体CustomerOrderOrderItemProductOrderItem 是一个关联表,将OrderProduct 连接起来,因此一个订单可以有多个产品。

Order 包含对客户的引用。

OrderItem 包含对 ProductOrder 的引用。

通过引用,我的意思是外键约束。

问题是当我尝试执行以下方法时,出现以下错误:

public static List<SalesStatistic> GetInvoices()
{
    using ApplicationDbContext context = new ApplicationDbContext();

    return context.Customers.Select(c => new SalesStatistic()
    {
        FirstName = c.FirstName,
        LastName = c.LastName,
        TotalPrice = context.Orders.Where(o => o.CustomerId == c.Id).Sum(oo => GetSalesPerOrder(oo.Nr))
    }).ToList();
}

System.InvalidOperationException: The LINQ expression 'DbSet<Order>()
    .Where(o => o.CustomerId == EntityShaperExpression: 
        Core.Entities.Customer
        ValueBufferExpression: 
            ProjectionBindingExpression: EmptyProjectionMember
        IsNullable: False
    .Id)
    .Sum(o => Repository.GetSalesPerOrder(o.Nr))' could not be translated. Additional information: Translation of method 'Persistence.Repository.GetSalesPerOrder' failed. If this method can be mapped to your custom function, see https://go.microsoft.com/fwlink/?linkid=2132413 for more information. 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.

GetSalesPerOrder 方法的工作原理是我为这些方法设置了单元测试。

public static double GetSalesPerOrder(string orderNr)
{
    using ApplicationDbContext context = new ApplicationDbContext();

    return context.Orders.Include(o => o.OrderItems).Where(o => o.Nr == orderNr).First().OrderItems!.Sum(oi => context.OrderItems.Include(o => o.Product).Where(oii => oii.Id == oi.Id).First().Product.Price * oi.Amount);
}

我试图修改GetInvoices,所以它不会调用GetSalesPerOrder,然后没有抛出异常。

我想知道我在上面的代码中做错了什么。

【问题讨论】:

  • GetSalesPerOrder(oo.Nr) 是一种方法。 EF 正在尝试将其转换为 SQL 查询,但它不知道如何处理该方法,因为它无法转换为 SQL。

标签: c# linq


【解决方案1】:

因为GetSalesPerOrder()不能翻译成SQL。这是您用 C# 编写的自定义方法。你什么能够做的是将该逻辑移动到表达式树中:

return context.Customers.Select(c => new SalesStatistic()
{
    FirstName = c.FirstName,
    LastName = c.LastName,
    TotalPrice = context.Orders
        .Where(o => o.CustomerId == c.Id)
        .Sum(oo => context.Orders
            .Include(o => o.OrderItems)
            .Where(o => o.Nr == oo.Nr)
            .First()
            .OrderItems!.Sum(oi => context.OrderItems
                .Include(o => o.Product)
                .Where(oii => oii.Id == oi.Id)
                .First()
                .Product.Price * oi.Amount))
}).ToList();

或者,如果您想将其重构为单独的方法,则该方法至少需要是 Func&lt;&gt;(或更可能是 Expression&lt;Func&lt;&gt;&gt;),以便 Entity Framework 将其转换为 SQL。可能最简单的方法是依靠 IDE 的重构工具通过突出显示 .Sum() 操作中的整个表达式并提取它来提取方法。

【讨论】:

  • 当我尝试执行您的代码时,(我之前也尝试过)我收到一条错误消息:Microsoft.Data.SqlClient.SqlException: Cannot perform an aggregate function on an expression containing an aggregate or a subquery. Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 2022-12-03
  • 2021-03-01
相关资源
最近更新 更多