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

实体框架Linq-如何获取包含所有数据的组,linq,entity-framework,group-by,Linq,Entity Framework,Group By,下面是一个示例数据集: OrderProduct是一个表,其中包含作为给定订单一部分的productID。 注意:OrderProduct是一个数据库表,我使用的是EF 订单ID,产品ID 1,1 2,2 3,4 3,5 4,5 4,2 5,2 5,3 我希望能够找到一个只包含我正在搜索的ProductID的订单。因此,如果我的输入是ProductIDS2,3,那么我应该返回OrderID5 我知道如何对数据进行分组,但我不确定如何对组执行选择 以下是我所拥有的: var q = from op

下面是一个示例数据集:

OrderProduct
是一个表,其中包含作为给定订单一部分的productID。 注意:OrderProduct是一个数据库表,我使用的是EF

订单ID,产品ID

1,1

2,2

3,4

3,5

4,5

4,2

5,2

5,3

我希望能够找到一个只包含我正在搜索的ProductID的订单。因此,如果我的输入是ProductIDS2,3,那么我应该返回OrderID5

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

以下是我所拥有的:

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

不知道如何从这里开始

乍一看,我会尝试以下方法:

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到SQL,而不是LINQ到实体,下面是另一种方法:

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

与使用单个LINQ语句不同,您基本上是逐段构建查询。

乍一看,我会尝试以下方法:

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

from o in context.Orders
where prodIds.All(pid => o.OrderProducts.Any(op => op.ProductId == pid))
select o
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);
通俗地说:“获取具有给定列表中每个ID的产品的订单。”

使现代化 由于您使用的似乎是LINQ到SQL,而不是LINQ到实体,下面是另一种方法:

var q = context.Orders;
foreach(var pid in prodIds)
{
    q = q.Where(o => o.OrderProducts.Any(op => op.ProductId == pid));
}
与使用单个LINQ语句不同,您基本上是逐段构建查询

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

第二个将有约15%的更好的性能(我已经检查过)

编辑

根据上一次的需求变更,您需要的订单不是包含您正在搜索的所有ProductID,而是包含这些ProductID,并且仅包含这些ProductID,我编写了一个更新版本:

 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);
因此,您唯一需要的是添加一个先决条件,确保集合包含相同数量的元素,它将适用于之前的两个查询,作为奖励,我建议使用最重要的where条件的3rd版本:

where orderGroup.Select(x => x.ProductId).Intersect(products).Count() == orderGroup.Count()

第二个将有约15%的更好的性能(我已经检查过)

编辑

根据上一次的需求变更,您需要的订单不是包含您正在搜索的所有ProductID,而是包含这些ProductID,并且仅包含这些ProductID,我编写了一个更新版本:

 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);
因此,您唯一需要的是添加一个先决条件,确保集合包含相同数量的元素,它将适用于之前的两个查询,作为奖励,我建议使用最重要的where条件的3rd版本:

where orderGroup.Select(x => x.ProductId).Intersect(products).Count() == orderGroup.Count()

多亏了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);
List prodIds=新列表{2,3};
var q=从订单中的o开始
//获取包含ProdId列表中产品的所有orderproducts
其中o.OrderProducts.All(op=>prodIds.Contains(op.ProductId))
//现在,按订单对OrderProducts进行分组
从o.OrderProducts中的op中选择
按op.OrderId将op分组到opGroup中
//仅选择与prodId列表具有相同计数的组
其中opGroup.Count()==prodIds.Count()
选择opGroup;
//清除任何可能为空的组
q=q.Where(fi=>fi.Count()>0);

(我使用的是LinqPad,这就是为什么查询看起来有点古怪-没有上下文等)

多亏了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);
List prodIds=新列表{2,3};
var q=从订单中的o开始
//获取包含ProdId列表中产品的所有orderproducts
其中o.OrderProducts.All(op=>prodIds.Contains(op.ProductId))
//现在,按订单对OrderProducts进行分组
从o.OrderProducts中的op中选择
按op.OrderId将op分组到opGroup中
//仅选择与prodId列表具有相同计数的组
其中opGroup.Count()==prodIds.Count()
选择opGroup;
//清除任何可能为空的组
q=q.Where(fi=>fi.Count()>0);

(我使用的是LinqPad,这就是为什么查询看起来有点古怪-没有上下文,等等)

Downvoter,请解释一下Downvoter。就我所知,这种方法应该满足要求。+1我不知道为什么有人会反对这种方法:对我来说,它看起来是一个可行的解决方案。StriplingWarrior,这也有一个“NotSupportedException:除了Contains运算符之外,不能在查询运算符的LINQ到SQL实现中使用本地序列”的问题。我认为这是因为“ALL”子句。您确定您使用的是EF吗,因为消息说它是LINQ to SQL。@Rajah:正如Restuta所指出的,您声称使用的是实体框架,但您报告的错误消息说它是LINQ to SQL。这两个框架是不同的,并且没有所有相同的特性。这个答案是有效的,因为它代表实体框架,但LINQ to SQL需要一种不同的方法,因为它不支持在内存列表中调用
。All
。Downvoter,请解释下一票。就我所知,这种方法应该满足要求。+1我不知道为什么有人会反对这种方法:对我来说,它看起来是一个可行的解决方案。StriplingWarrior,这也有一个“NotSupportedException:除了Contains运算符之外,不能在查询运算符的LINQ到SQL实现中使用本地序列”的问题。我认为这是因为“ALL”子句。您确定您使用的是EF吗,因为消息说它是LINQ to SQL。@Rajah:正如Restuta所指出的,您声称使用的是实体框架,但您报告的错误消息说它是LINQ to SQL。这两个框架是不同的,并且没有所有相同的特性。这个答案是有效的,因为它代表实体框架,但是LINQ to SQL需要一种不同的方法,因为它不支持在内存列表上调用
.All
。Restuta,这个查询与dasblinkenlight的答案有相同的问题。产品。除了使用EF时不能使用。哦,我明白了,是吗