GroupJoin:引发的异常:System.InvalidOperationException

时间:2019-07-10 18:03:55

标签: entity-framework entity-framework-core ef-core-2.2

我正尝试编写一个查询以获取所有餐厅表(如果存在)或是否未进行开盘销售。 如果桌上有一笔买卖,我想获取总和和几个详细信息。那是我的代码:

   db.SALETABLES
  .GroupJoin(
        db.SALES.Where(c => c.CLOSEDTIME == null),
        t => t.ID,
        sa => sa.ID_TABLE,
     (ta, s) => new
            {
               ta.ID,
               ta.DESCRIPTION,
                NR_SALE = s.Any() ? s.First().NR_SALE : 0,
                IDSALE = s.Any() ? s.First().ID : 0,
                IDUSER = s.Any() ? s.First().IDUSER : 0,
                USERNAME = s.Any() ? s.First().USERS.USERNAME :"" ,
                SALESUM = s.Any() ? s.First().SALES_DETAIL.Sum(p => p.PRICE * p.CANT) : 0
                         }

但出现此错误:

  

抛出异常:“ System.InvalidOperationException”   System.Private.CoreLib.dll

感谢您的帮助

2 个答案:

答案 0 :(得分:3)

由于EF Core基础架构例外,显然您正在遇到当前的EF Core实现错误。

但是您可以通过在编写LINQ to Entities查询时遵循一些规则来帮助EF Core查询翻译器(从而避免因缺少用例而导致的错误)。在大多数情况下,这些规则还将消除客户端对查询的评估(或EF Core 3.0+中的例外)。

与该特定查询相关的问题之一就是-从不使用First。如果集合为空,First的LINQ to Objects行为将引发异常。这对于自然支持并返回NULL的SQL是不自然的,即使对于通常不允许NULL的值也是如此。为了模拟LINQ to Objects行为,EF Core必须评估First()客户端,即使它起作用也不好。相反,请使用与SQL具有相同语义的FirstOrDefault(),从而进行翻译。

回顾一下,当您需要将结果作为单个“对象”或FirstOrDefault()时使用null,或者当您希望将结果作为0或1的集合时使用Take(1)元素。

在这种情况下,最好将0或1相关的SALE规则直接合并到联接子查询中,方法是删除GroupJoin并用相关的{{ 1}}。并且SelectMany支票被Where支票取代。

话虽如此,修改后的工作且完全由服务器翻译的查询如下所示:

Any()

答案 1 :(得分:2)

您没有指定异常,但是我认为它与客户端评估(CSE)有关,并且您配置了EF在发生异常时抛出异常。

可能是First()触发了CSE或GroupJoin。前者可以使用FirstOrDefault()轻松修复。 GroupJoin还有更多功能。

在很多情况下,完全不需要使用GroupJoin中的Join。通常,手动编码的联接可以并且应该被导航属性代替。这不仅使代码更易于阅读,而且避免了EF 2.x在GroupJoin中遇到的两个问题。

您的SaleTable类(我不会遵循您的数据库驱动名称)应该具有属性Sales

public ICollection<Sale> Sales { get; set; }

如果愿意,Sale可以具有反向导航属性:

public SaleTable SaleTable { get; set; }

配置为

modelBuilder.Entity<SaleTable>()
    .HasMany(e => e.Sales)
    .WithOne(e => e.SaleTable)
    .HasForeignKey(e => e.SaleTableId) // map this to ID_TABLE
    .IsRequired();

现在使用表的Sales属性将具有与GroupJoin相同的效果—唯一的键(这里是SaleTable,具有自己的集合),但是没有问题。

下一个改进是简化查询。有两种方式。 1.您反复访问第一个Sale,因此使用let语句。 2.查询已翻译为SQL,因此不必担心null 引用,但要为null 做准备。改进的查询将阐明我的意思。

var query = from st in db.SaleTables
            let firstSale = st.Sales.FirstOrDefault()
            select new
            {
                st.ID,
                NrSale = (int?)firstSale.NrSale ?? 0,
                IdSale = (int?)firstSale.ID ?? 0,
                ...
                SalesSum = (int?)firstSale.SalesDetails.Sum(p => p.Price * p.Cant) ?? 0
            }

如果使用NrSale = firstSale.NrSale,会引发SaleTable的不包含Sales的异常(可为空的对象必须具有值)。