实体框架Linq - 如何获取包含所有数据的组

时间:2012-02-10 02:14:49

标签: linq entity-framework group-by

这是一个示例数据集:

OrderProduct是一个表,其中包含属于给定订单的productIds。 注意 OrderProduct是一个数据库表,我正在使用EF。

OrderId,ProductId

1,1

2,2

3,4

3,5

4,5

4,2

5,2

5,3

我希望能够做的是找到一个包含我正在搜索的productIds的订单。因此,如果我的输入是productIds 2,3,那么我应该回到OrderId 5。

我知道如何对数据进行分组,但我不确定如何对该组执行选择。

这就是我所拥有的:

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

不确定如何从这里开始

3 个答案:

答案 0 :(得分:2)

乍一看,我会尝试这样的事情:

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

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

用简单的语言:“获取包含给定列表中每个ID的产品的订单。”

更新

由于看起来您使用的是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语句,而是基本上构建查询零碎。

答案 1 :(得分:2)

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%(我已经检查过了)

修改

根据上一次的需求变更,您需要的订单不包含您正在搜索的所有productIds,但正是那些而且只是那些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 :(得分:0)

感谢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,这就是为什么查询看起来有点时髦 - 没有上下文等)