我正尝试编写一个查询以获取所有餐厅表(如果存在)或是否未进行开盘销售。 如果桌上有一笔买卖,我想获取总和和几个详细信息。那是我的代码:
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
感谢您的帮助
答案 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
的异常(可为空的对象必须具有值)。