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。
我认为你只需要一个JOIN
和WHERE
就可以做你正在寻找的东西 – 你可以用IN
子句来做,但我认为EXISTS
查询是查找数据最正确的方法。
在任何情况下,关于entity framework的一个奇妙的事情是你不必担心生成什么 – 只要LINQ语句没问题,那么它将找到编写查询的最佳方式,你应该永远不要去看它。 当您执行分页和其他类似的事情时,尤其如此,LINQ很简单,但生成的SQL非常难看。