3

我有一个对象的导航属性,比如 Items,比如 Order。如果我想获取订单列表,包括商品,我可以执行以下操作:

var orders = dbContext.Order.Include(o => i.Items);

这很好用,但现在我只想为每个订单购买 3 件商品,我想知道实现这一目标的最佳方式。

一种方法是执行以下操作:

var orders = 
    (from o in dbContext.Order
     join i in dbContext.Items on o.Id equals i.OrderId
     select new { o.Id, i })
    .GroupBy(o => o.Id)
    .SelectMany(i => i.Take(3))

这很好用,虽然生成的 SQL 有点复杂,但我想知道是否有更直接(或高性能)的方式。

谢谢,埃里克

4

3 回答 3

3

性能有多差?如果可以忍受,那我就不管了。在 SQL 中也没有直接的方法可以做到这一点。通常你会得到一个子查询来计算ROW_NUMBER你的分组分区,然后返回行号小于的行n

由于没有将该机制直接转换为 Linq,因此我会保持 Linq 易于理解,并且不必担心生成的 SQL 的复杂性,除非它是一个重要的性能问题。

您还可以将其与返回所有项目然后使用 Linq-to-Objects 过滤的性能进行比较。

另一种选择是将其编码为存储过程,而不是尝试在 Linq 中进行。

于 2013-03-11T22:24:12.017 回答
3

OUTER APPLY这应该以对 Items的 with top 3 语句的形式生成简单的 SQL 。然后我们必须使用 linq-to-objects 进行一些分组,但只有我们需要的数据来自服务器。

var orders = 
   (from o in dbContext.Order
    from i in (from x in dbContext.Items
               where o.Id == x.OrderId
               select x).Take(3).DefaultIfEmpty()
    select new
    {
        Order = o,
        Item = i
    }).AsEnumerable()
      .GroupBy(x => x.Order)
      .Select(x => new { Order = x.Key, Items = x.Select (y => y.Item ) });

如果您只想要每个订单的前 3 件商品而没有订单实体。将在 SQL 中生成CROSS APPLYwith top 语句到项目。

var items = 
      from o in dbContext.Order
      from i in (from x in dbContext.Items
                 where o.Id == x.OrderId
                 select x).Take(3)
      select i;
于 2013-03-11T22:50:16.620 回答
3
var orders = dbContext.Order
    .Select(o => new
    {
        Order = o,
        Items = o.Items.Take(3)
    })
    .AsEnumerable()
    .Select(a => a.Order)
    .ToList();

Order.Items这将自动用前 3 个项目填充集合,如果

  • 您没有禁用更改跟踪(不是上面查询中的情况)
  • Order和之间的关系Item不是多对多的(可能不是这种情况,因为订单和商品通常是一对多的关系)

编辑

生成的 SQL 查询为:

SELECT 
[Project2].[Id] AS [Id], 
[Project2].[C1] AS [C1], 
[Project2].[Id1] AS [Id1], 
[Project2].[OrderId] AS [OrderId], 
FROM (SELECT 
      [Extent1].[Id] AS [Id], 
      [Limit1].[Id] AS [Id1], 
      [Limit1].[OrderId] AS [OrderId], 
      CASE WHEN ([Limit1].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
      FROM  [dbo].[Orders] AS [Extent1]
      OUTER APPLY  (SELECT TOP (3) 
          [Extent2].[Id] AS [Id], 
          [Extent2].[OrderId] AS [OrderId], 
          FROM [dbo].[Items] AS [Extent2]
          WHERE [Extent1].[Id] = [Extent2].[OrderId] ) AS [Limit1]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC
于 2013-03-11T23:38:57.183 回答