Entity Framework Linq - как получить группы, содержащие все ваши данные - PullRequest
2 голосов
/ 10 февраля 2012

Вот пример набора данных:

OrderProduct - это таблица, содержащая идентификаторы товаров, которые были частью данного заказа. Примечание : OrderProduct - это таблица базы данных, и я использую EF.

OrderId, ProductId

1, 1

2, 2

3, 4

3, 5

4, 5

4, 2

5, 2

5, 3

Что я хочу сделать - это найти заказ, который содержит только идентификаторов товаров, которые я ищу. Так что, если мой ввод был productIds 2,3, то я должен вернуть OrderId 5.

Я знаю, как я могу сгруппировать данные, но я не уверен, как выполнить выборку для группы.

Вот что у меня есть:

var q = from op in OrderProduct
group op by op.OrderId into orderGroup
select orderGroup;

Не уверен, как действовать дальше

Ответы [ 3 ]

2 голосов
/ 10 февраля 2012
IEnumerable<int> products = new List<int> {2, 3};

IEnumerable<OrderProduct> orderProducts = new List<OrderProduct>
{
   new OrderProduct(1, 1), 
   new OrderProduct(2, 2), 
   new OrderProduct(3, 4), 
   new OrderProduct(3, 5), 
   new OrderProduct(4, 5), 
   new OrderProduct(4, 2), 
   new OrderProduct(5, 2), 
   new OrderProduct(5, 3),
};

var orders =
   (from op in orderProducts
    group op by op.OrderId into orderGroup
    //magic goes there
    where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
    select orderGroup);

//outputs 5
orders.Select(x => x.Key).ToList().ForEach(Console.WriteLine);

Или у вас может быть другая версия, указанная в другом ответе, просто замените

 where !products.Except(orderGroup.Select(x => x.ProductId)).Any()

на

 where products.All(pid => orderGroup.Any(op => op.ProductId == pid))

, вторая будет иметь ~ 15% лучшую производительность (Iпроверил это)

Редактировать

Согласно последнему изменению требования, вам нужны заказы, которые содержат не все идентификаторы товаров, которые вы ищете, а именно те и только теproductIds, я написал обновленную версию:

 var orders =
       (from op in orderProducts
        group op by op.OrderId into orderGroup
        //this line was added
        where orderGroup.Count() == products.Count()
        where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
        select orderGroup);

Так что единственное, что вам нужно, это добавить предварительное условие, гарантирующее, что коллекции содержат одинаковое количество элементов, это будет работать для обоих предыдущие запросы, и в качестве бонуса я предлагаю 3-й вариант наиболее важного условия:

where orderGroup.Select(x => x.ProductId).Intersect(products).Count() == orderGroup.Count()
2 голосов
/ 10 февраля 2012

На первый взгляд, я бы попробовал что-то вроде этого:

var prodIds = new[] {2, 3};

from o in context.Orders
where prodIds.All(pid => o.OrderProducts.Any(op => op.ProductId == pid))
select o

На простом языке: «получить заказы, в которых есть продукт с каждым идентификатором в данном списке.»

Обновление

Поскольку оказывается, что вы используете LINQ to SQL, а не LINQ to Entities, вот другой подход:

var q = context.Orders;
foreach(var pid in prodIds)
{
    q = q.Where(o => o.OrderProducts.Any(op => op.ProductId == pid));
}

Вместо того чтобы использовать один оператор LINQ, вы по существу строите фрагмент запроса.

0 голосов
/ 10 февраля 2012

Благодаря ответу StriplingWarrior мне удалось это выяснить. Не уверен, что это лучший способ сделать это, но он работает.

List<int> prodIds = new List<int>{2,3};

var q = from o in Orders
//get all orderproducts that contain products in the ProdId list
where o.OrderProducts.All(op => prodIds.Contains(op.ProductId))
//now group the OrderProducts by the Orders
select from op in o.OrderProducts 
group op by op.OrderId into opGroup
//select only those groups that have the same count as the prodId list
where opGroup.Count() == prodIds.Count()
select opGroup;

//get rid of any groups that may be empty
q = q.Where(fi => fi.Count()> 0);

(я использую LinqPad, поэтому запрос выглядит немного странно - без контекста и т. Д.)

...