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?
Don't know if this will work with LINQ to SQL, but you could try
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()
};
which I'm fairly sure would work in Entity Framework.
I managed to work this out myself. It's not exactly memory-friendly, but it works:
// 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)
});
Not exactly concise, and extremely hacky in places, but it does the job. I get a total of 6 queries, instead of thousands.
Try performing the group joins before the inner joins. That way you're performing a group join for each orderItem
instead of for each inner join result:
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
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.