【问题标题】:Selecting IEnumerable from JOIN results in a complex Linq-to-SQL query从 JOIN 中选择 IEnumerable 会导致复杂的 Linq-to-SQL 查询
【发布时间】:2012-10-05 07:58:04
【问题描述】:

我有一个如下所示的查询:

var orderLines = from Order order in _orders
                 join OrderItem orderItem in dbc.OrderItems
                     on order.OrderId equals orderItem.OrderId
                 join Product product in dbc.Products
                     on orderItem.ProductId equals product.ProductId
                 join OrderItemQuantity oiq in dbc.OrderItemQuantities
                     on orderItem.OrderItemId equals oiq.OrderItemId
                     into orderItemQuantities
                 join ActivityQuantity aq in dbc.ActivityQuantities
                     on orderItem.OrderItemId equals aq.OrderItemId
                     into activityQuantities
                 orderby
                     order.OrderId ascending,
                     orderItem.OrderLineNumber ascending
                 select new {
                     Order = order,
                     Item = orderItem,
                     Product = product,
                     // I'd like to get these child items as IEnumerables or similar
                     ItemQuantities = orderItemQuantities,
                     ActivityQuantities = activityQuantities
                 };

这编译得很好,但会导致查询中缺少 orderItemQuantitiesactivityQuantities 部分,所以我得到一个用于订单/项目/产品的选择/加入/加入,并在 OIQ/AQ 上单独选择对于每个条目:

SELECT (...) FROM [ORDERS] AS t0
INNER JOIN [ORDER_ITEMS] AS t1 ON t0.ORDER_ID = t1.ORDER_ID
INNER JOIN [PRODUCTS] AS t2 ON t1.PRODUCT_ID = t2.PRODUCT_ID
ORDER BY (...)

然后,对于这些行中的每一行,它执行以下查询:

SELECT (...) FROM [ACTIVITY_QUANTITY] as t0
WHERE t0.ORDER_ITEM_ID = @p0

SELECT (...) FROM [ORDER_ITEM_QUANTITY] as t0
WHERE t0.ORDER_ITEM_ID = @p0

由于我有数万行,这会导致大量查询。

有没有办法将OrderItemOrderItemQuantityActivityQuantity 条目合并到IEnumerable(或类似的)中,以便从选择中使用的匿名类型轻松访问它们?

【问题讨论】:

    标签: c# linq linq-to-sql ienumerable


    【解决方案1】:

    尝试在内部连接之前执行组连接。这样您就可以为每个 orderItem 而不是每个内部连接结果执行组加入:

    var orderLines = from OrderItem orderItem in dbc.OrderItems
                     join OrderItemQuantity oiq in dbc.OrderItemQuantities
                         on orderItem.OrderItemId equals oiq.OrderItemId
                         into orderItemQuantities
                     join ActivityQuantity aq in dbc.ActivityQuantities
                         on orderItem.OrderItemId equals aq.OrderItemId
                         into activityQuantities
                     join Order order in _orders
                         on orderItem.OrderId equals order.OrderId
                     join Product product in dbc.Products
                         on orderItem.ProductId equals product.ProductId
                     orderby
                         order.OrderId ascending,
                         orderItem.OrderLineNumber ascending
                     select new {
                         Order = order,
                         Item = orderItem,
                         Product = product,
                         // I'd like to get these child items as IEnumerables or similar
                         ItemQuantities = orderItemQuantities,
                         ActivityQuantities = activityQuantities
                     };
    

    【讨论】:

      【解决方案2】:

      我自己设法解决了这个问题。它并不完全对内存友好,但它可以工作:

      // build a base query that only selects the order, item and product entities
      var orderLinesBase = from Order order in _orders
                   join OrderItem orderItem in dbc.OrderItems
                       on order.OrderId equals orderItem.OrderId
                   join Product product in dbc.Products
                       on orderItem.ProductId equals product.ProductId
                   orderby
                       order.OrderId ascending,
                       orderItem.OrderLineNumber ascending
                   select new {
                       Order = order,
                       Item = orderItem,
                       Product = product
                   };
      
      // get all OrderItemQuantity entities that are applicable to the orderLinesBase query
      var orderItemQuantities = (from Order in _orders
                                 join OrderItem orderItem in dbc.OrderItems
                                     on order.OrderId equals orderItem.OrderId
                                 join OrderItemQuantity oiq in dbc.OrderItemQuantities
                                     on orderItem.OrderItemId equals oiq.OrderItemId
                                 select oiq).ToArray();
      
      // get all ActivityQuantity entities that are applicable to the orderLinesBase query
      var activityQuantities = (from Order in _orders
                                 join OrderItem orderItem in dbc.OrderItems
                                     on order.OrderId equals orderItem.OrderId
                                 join ActivityQuantity aq in dbc.ActivityQuantities
                                     on orderItem.OrderItemId equals aq.OrderItemId
                                 select aq).ToArray();
      
      // notice that the above two queries use ToArray to force evaluation of the expression.
      
      // this is just some cast by example trickery, to help with anonymous types
      // it's just this method: List<T> CastListByExample<T>(T t) { return new List<T>(); }
      var orderLines = Helpers.CastListByExample(new {
          Order = (Order)null,
          Item = (OrderItem)null,
          Product = (Product)null,
          ItemQuantities = (IEnumerable<OrderItemQuantity>)null,
          ActivityQuantities = (IEnumberable<ActivityQuantity>)null });
      
      // manually fill the list
      foreach (var orderLine in orderLinesBase)
          orderLines.Add(new
          {
              Order = orderLine.Order,
              Item = orderLine.Item,
              Product = orderLine.Product,
              // use a method to filter the quantity data sets manually, because
              // LINQ won't work with memory-backed enumerables
              ItemQuantities = FilterByOrderItemId(orderItemQuantities, orderLine.Item.OrderItemId),
              ActivityQuantities = FilterByOrderItemId(activityQuantities, orderLine.Item.OrderItemId)
          });
      

      不完全简洁,而且在某些地方非常hacky,但它可以完成工作。我总共收到 6 个查询,而不是数千个。

      【讨论】:

        【解决方案3】:

        不知道这是否适用于 LINQ to SQL,但您可以尝试

                     select new {
                         Order = order,
                         Item = orderItem,
                         Product = product,
                         // I'd like to get these child items as IEnumerables or similar
                         ItemQuantities = orderItemQuantities.ToList(),
                         ActivityQuantities = activityQuantities.ToList()
                     };
        

        我相当肯定它可以在实体框架中工作。

        【讨论】:

        • 这不是简单地创建一个带有延迟枚举器的IList&lt;T&gt;,每次调用IEnumerator.MoveNext 时仍会执行单个查询? 更新:是的,已验证它确实只是将底层枚举器转换为 IList&lt;T&gt;,并且查询仍然是独立的。
        • @Polynomial 很可惜。然后删除这个答案。 EF 为您制作清单 :(
        • 不要删除答案。它可能会帮助其他人排除这种可能性。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-04
        相关资源
        最近更新 更多