Linq to Entities有多对多选择:如何强制生成JOIN而不是subselect子句?

在此处输入图像描述

首先使用EF DB我有两个具有多对多关系的实体(供应商,产品)。 entity framework不为关联表(SupplierProduct)创建实体,因为关联表仅包含强实体的主键。

我一直在为所有不提供给定产品的供应商提供以下查询:

var q1 = context.Suppliers.Where(s=>!s.Products.Any(p=>p.Id == 1)); 

生成的SQL使用与此类似的EXISTS依赖子查询:

 SELECT * FROM Suppliers s WHERE NOT EXISTS (SELECT 1 FROM SupplierProduct sp WHERE sp.SupplierId = s.Id && sp.ProductId = 1) 

是否可以使用Linq to Entities方法语法生成在关联表上使用连接的查询?

即:

 SELECT DISTINCT s.* FROM SupplierProduct sp JOIN Supplier s ON s.Id = sp.SupplierId; WHERE sp.ProductId != 1 

更新

正如JoeEnos所指出的,我上面的查询并没有做同样的事情。 NOT EXISTS子查询可能是最好的方法。 如果我试图让所有供应商提供产品怎么办? 我会将我的linq更改为实体查询稍微:

 var q1 = context.Suppliers.Where(s => s.Products.Any(p=>p.Id == 1)); 

并且生成的SQL将是:

 SELECT * FROM Suppliers s WHERE EXISTS (SELECT 1 FROM SupplierProduct sp WHERE sp.SupplierId = s.Id && sp.ProductId = 1) 

哪个好,我得到了我想要的结果。 但是,如果我在这种情况下编写SQL,我通常会这样做:

 SELECT s.* FROM SupplierProduct sp JOIN Supplier s ON s.Id = sp.SupplierId; WHERE sp.ProductId = 1 

可以将我的linq to entities查询更改为生成上述SQL吗?

要生成使用连接而不是EXISTS的SQL,当根据与其他实体的m:n关联选择实体时,可以使用SelectMany() 。 例如:

 var q1 = context.Suppliers.Where(s => s.Products.Any(p=>p.Id == 1)); 

可以改写为:

 var q1 = context.Products.Where(p => p.Id == 1).SelectMany(p => p.Suppliers); 

你的两个查询做了很多不同的事情。 您的Any / EXISTS查询会获得根本没有产品1的供应商。 您的JOIN查询将获得所有非1的产品的供应商,无论他们是否也有产品1。

我认为你只需要一个JOINWHERE就可以做你正在寻找的东西 – 你可以用IN子句来做,但我认为EXISTS查询是查找数据最正确的方法。

在任何情况下,关于entity framework的一个奇妙的事情是你不必担心生成什么 – 只要LINQ语句没问题,那么它将找到编写查询的最佳方式,你应该永远不要去看它。 当您执行分页和其他类似的事情时,尤其如此,LINQ很简单,但生成的SQL非常难看。