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

时间:2013-03-11 22:19:34

标签: c# linq entity-framework entity-framework-5

我有一个对象的导航属性,例如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有点复杂,但我想知道是否有更直接(或高性能)的方式。

谢谢, 埃里克

3 个答案:

答案 0 :(得分:3)

表现有多糟糕?如果它是可以忍受的那么我就不管它了。在SQL中也没有直接的方法来做到这一点。通常,您最终会得到一个子查询,用于计算分组所分区的ROW_NUMBER,然后返回行号小于n的行。

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

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

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

答案 1 :(得分:3)

这应该以{{1​​}}的形式生成简单的SQL,其中包含对项目的前3个语句。然后我们必须使用linq-to-objects进行一些分组,但只有我们需要的数据来自服务器。

OUTER APPLY

如果您只想要每个订单的前三个项目而没有订单实体。将SQL中的top语句生成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 ) }); 项。

CROSS APPLY

答案 2 :(得分:3)

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

如果

,这将自动填充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