2

I've got a query that looks like this:

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
                 };

This compiles fine, but results in the orderItemQuantities and activityQuantities parts being missing from the query, so I get a single select/join/join for order/items/products, and separate individual selects on OIQ/AQ for each entry:

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 (...)

Then, for each of those rows, it performs these queries:

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

Since I've got tens of thousands of rows, this results in a ridiculous number of queries.

Is there a way to coalesce the OrderItemQuantity and ActivityQuantity entries for the OrderItem into an IEnumerable (or similar) in a way that makes them easily accessible from the anonymous type used in the select?

4

3 回答 3

0

我自己设法解决了这个问题。它不完全对内存友好,但它有效:

// 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 个查询,而不是数千个。

于 2012-10-15T15:01:59.060 回答
0

不知道这是否适用于 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()
             };

我相当肯定会在实体框架中工作。

于 2012-10-15T10:50:31.317 回答
0

尝试在内部连接之前执行组连接。这样您就可以为每个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
                 };
于 2012-10-15T22:27:12.513 回答