我目前正在使用SQL Server的EFCore 1.1(预览版)。
我正在做我认为是Order
和OrderItem
表之间的一个简单的OUTER JOIN。
var orders = from order in ctx.Order
join orderItem in ctx.OrderItem
on order.OrderId equals orderItem.OrderId into tmp
from oi in tmp.DefaultIfEmpty()
select new
{
order.OrderDt,
Sku = (oi == null) ? null : oi.Sku,
Qty = (oi == null) ? (int?) null : oi.Qty
};
返回的实际数据是正确的(我知道早期版本存在OUTER join根本无法工作的问题)。然而,SQL是可怕的,包括Order
和OrderItem
的每一列,考虑到其中一个是一个大的XML Blob,这是有问题的。
选择(秩序)。(OrderId),(顺序)。[OrderStatusTypeId],(订单)。[OrderSummary],(顺序)。[OrderTotal],[顺序]。[OrderTypeId],(订单)。[ParentFSPId],[顺序]。[ParentOrderId],(订单)。[PayPalECToken],(顺序)。[PaymentFailureTypeId]…
…[orderItem]。[OrderId], [orderItem]。[OrderItemType], [orderItem]。(数量),[orderItem]。[SKU]来自[Order] AS [Order]左连接[OrderItem] AS[orderItem] ON [order]。[OrderId] = [orderItem]。[OrderId]排序(顺序)。(OrderId)
(这里没有显示更多的列)
另一方面,如果我使它成为一个INNER JOIN,那么SQL就像预期的那样只有我的select子句中的列:
选择(秩序)。[OrderDt], [orderItem]。[SKU], [orderItem]。(数量)从[Order]作为[Order]内部连接[OrderItem]作为[OrderItem] ON(订单)。[OrderId] = [orderItem].[OrderId]
我尝试恢复到EFCore 1.01,但得到一些可怕的nuget包错误,放弃了。
不清楚这是实际的回归问题还是EFCore中不完整的功能。但是在其他地方找不到更多的信息。
编辑:EFCore 2.1已经解决了很多问题与分组和N+1类型的问题,其中一个单独的查询是为每个子实体。事实上,我对演出印象非常深刻。
3/14/18 -不推荐EFCore的2.1预览1,因为在使用OrderBy()时,GROUP BY SQL有一些问题,但在夜间构建和预览2中已经修复。
以下内容适用于EF Core 1.1.0(发行版)。
虽然不应该做这样的事情,尝试了几种替代语法查询(使用导航属性代替手动连接,连接包含匿名类型投影的子查询,使用let
/中间Select
,使用Concat
/Union
来模拟左连接,替代左连接语法等)的结果-无论是相同的帖子,和/或执行多个查询,和/或无效的SQL查询,和/或奇怪的运行时异常,如IndexOutOfRange
, InvalidArgument
等。
根据测试,我能说的是,最有可能的问题是与GroupJoin
翻译中的bug(回归,不完整的实现-这真的重要吗)有关。例如,#7003:在最终投影中不存在的子查询上使用组连接生成错误的SQL; #6647 -左连接(GroupJoin)总是物化元素,导致不必要的数据拉出等。
直到它得到修复(何时?),作为一个(远非完美的)解决方案,我可以建议使用替代的左外连接语法(from a in A from b in B.Where(b = b.Key == a.Key).DefaultIfEmpty()
):
var orders = from o in ctx.Order
from oi in ctx.OrderItem.Where(oi => oi.OrderId == o.OrderId).DefaultIfEmpty()
select new
{
OrderDt = o.OrderDt,
Sku = oi.Sku,
Qty = (int?)oi.Qty
};
生成如下SQL:
SELECT [o].[OrderDt], [t1].[Sku], [t1].[Qty]
FROM [Order] AS [o]
CROSS APPLY (
SELECT [t0].*
FROM (
SELECT NULL AS [empty]
) AS [empty0]
LEFT JOIN (
SELECT [oi0].*
FROM [OrderItem] AS [oi0]
WHERE [oi0].[OrderId] = [o].[OrderId]
) AS [t0] ON 1 = 1
) AS [t1]
正如您所看到的,投影是可以的,但是它使用了奇怪的CROSS APPLY
而不是LEFT JOIN
,这可能会引入另一个性能问题。
还请注意,在如上所示访问右连接表时,必须对值类型使用强制类型转换,而对字符串不使用强制类型转换。如果在原始查询中使用null
检查,则在运行时将得到ArgumentNullException
(这又是一个错误)。
使用"into"将创建一个临时标识符来存储结果。
Reference: MDSN: into (c# Reference)
因此,删除" into tmp from oi in tmp.DefaultIfEmpty()
"将导致具有三列的干净sql。
var orders = from order in ctx.Order
join orderItem in ctx.OrderItem
on order.OrderId equals orderItem.OrderId
select new
{
order.OrderDt,
Sku = (oi == null) ? null : oi.Sku,
Qty = (oi == null) ? (int?) null : oi.Qty
};