使用LINQ,获得导航属性的前x行有哪些方法?

本文关键字:方法 LINQ 属性 导航 使用 | 更新日期: 2023-09-27 18:03:20

我有一个导航属性,名称为Items,对象名称为Order。如果我想获得订单列表,包括Items,我可以这样做:

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有点复杂,但我想知道是否有更直接(或性能)的方式。

谢谢,Eric

使用LINQ,获得导航属性的前x行有哪些方法?

var orders = dbContext.Order
    .Select(o => new
    {
        Order = o,
        Items = o.Items.Take(3)
    })
    .AsEnumerable()
    .Select(a => a.Order)
    .ToList();

如果

,这将自动用前3个项目填充Order.Items集合
  • 你不禁用更改跟踪(上面的查询不是这种情况)
  • OrderItem之间的关系不是多对多的(可能不是这种情况,因为订单和商品通常是一对多的关系)

编辑

生成的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

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

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

您还可以将其与返回所有项然后使用链接到对象进行过滤的性能进行比较。

另一个选择是将其编码为存储过程,而不是尝试在Linq中完成。

这将生成OUTER APPLY形式的简单SQL,其中top 3语句为Items。然后,我们必须使用链接到对象进行一些分组,但只有我们需要的数据已经从服务器带来了。

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个项目,而不需要订单实体。将生成CROSS APPLY与SQL中的top语句到items。

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;