具有多个连接条件的实体框架查询
Posted
技术标签:
【中文标题】具有多个连接条件的实体框架查询【英文标题】:Entity Framework Query with multiple join conditions 【发布时间】:2020-11-10 02:19:50 【问题描述】:已编辑
我有表Customers
、Sites
、Buildings
和Addresses
。
每个客户都有零个或多个(一个?)站点,每个站点都是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个站点都有零个或多个建筑物,每个建筑物都在一个站点上,即外键 Building.SiteId
所指的站点。
最后:每个客户/站点/建筑物只有一个地址,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指的地址。
我还有一个string searchText
我想要至少具有以下一项的所有客户的 ID:
CustomerName
类似于 searchText
至少有一个SiteName
类似于searchText
至少有一个BuildingName
,类似于searchText
PhysicalAddress
喜欢 searchText
所有Sites
中的至少一个SiteAddress
类似于searchText
所有Buildings
中的至少一个BuildingAddress
类似于searchText
对于上述要求,我有这个 SQL 查询逻辑
SELECT DISTINCT c.customerID
FROM Customer AS c
LEFT OUTER JOIN Site AS s ON s.customerId = c.customerID
LEFT OUTER JOIN Building AS b ON s.Id = b.siteId
LEFT OUTER JOIN Address AS A ON A.addressId = c.customerPhysicalAddressID
OR A.addressId = s.AddressId
OR A.addressId = b.buildingAddressId
WHERE
c.customerName LIKE '%searchText%'
OR c.SiteName LIKE '%searchText%'
OR b.buildingName LIKE '%searchText%'
OR A.Street LIKE '%searchText%'
在编写 linq 查询时,控制器类出现问题。
我的 Linq 查询是这样写的
if (!string.IsNullOrEmpty(searchText))
var resultQuery = from customer in this.DatabaseContext.Customers
join site in this.DatabaseContext.Sites
on customer.customerID equals site.CustomerId into customer_site_group
from customer_site in customer_site_group.DefaultIfEmpty()
join building in this.DatabaseContext.Buildings
on customer_site.Id equals building.siteId into site_building_group
from site_building in site_building_group.DefaultIfEmpty()
join A in this.DatabaseContext.Addresses
on new
key1 = customer.customerPhysicalAddressID,
key2 = customer_site.AddressId,
key3 = site_building.buildingAddressID
equals new
key1 = A.addressID ||
key2 = A.addressID ||
key3 = A.addressID
into Address_site_building
where (customer.customerName.Contains(searchText) ||
customer_site.siteName.Contains(searchText) ||
site_building.buildingName.Contains(searchText) ||
A.street.Contains(searchText))
select new
customerID = customer.customerID
;
在结果查询中,我只想让客户 ID 满足上述条件。在引入Addresses
实体之前,linq 查询工作正常。面对写入多个on条件,LinqPad报错
连接子句中的表达式之一的类型不正确。调用 GroupJoin 时类型引用失败
我是 EF 和 linq 的新手 - 只是尝试并理解它。
感谢任何有价值的 cmets 和答案。
【问题讨论】:
为什么卡住了?它不做你想做的事吗?你有编译器错误吗? 首先,使用customer.Sites
、site.Buildings
、...等导航属性,然后只需使用from address in Addresses where ... || ... || ...
。
@GertArnold 你能详细说明一下吗?
【参考方案1】:
如果您看到 SQL 查询可以重写为,答案可能很明显
SELECT DISTINCT c.customerID
FROM Customer AS c
LEFT OUTER JOIN Site AS s ON s.customerId = c.customerID
LEFT OUTER JOIN Building AS b ON s.Id = b.siteId
, Address AS A
WHERE
(c.customerName LIKE '%searchText%'
OR c.SiteName LIKE '%searchText%'
OR b.buildingName LIKE '%searchText%'
OR A.Street LIKE '%searchText%')
AND (A.addressId = c.customerPhysicalAddressID
OR A.addressId = s.AddressId
OR A.addressId = b.buildingAddressId)
即连接变成了WHERE
子句。然后 LINQ 翻译就变成了类似
from customer in this.DatabaseContext.Customers
join site in this.DatabaseContext.Sites
on customer.customerID equals site.CustomerId into customer_site_group
from customer_site in customer_site_group.DefaultIfEmpty()
join building in this.DatabaseContext.Buildings
on customer_site.Id equals building.siteId into site_building_group
from site_building in site_building_group.DefaultIfEmpty()
from A in this.DatabaseContext.Addresses
where (customer.customerPhysicalAddressID = A.addressID
|| customer_site.AddressId = A.addressID
|| site_building.buildingAddressID = A.addressID)
where (customer.customerName.Contains(searchText) ||
customer_site.siteName.Contains(searchText) ||
site_building.buildingName.Contains(searchText) ||
A.street.Contains(searchText))
select new
customerID = customer.customerID
;
一般建议(请参阅我的评论):尝试通过引入导航属性从 LINQ 查询中删除连接。
【讨论】:
我只是从您的代码中复制了它,所以不知道为什么它不应该工作。也许street
不是字符串而是实体?【参考方案2】:
所以你有Customers
、Sites
、Buildings
和Addresses
的表格。
每个客户都有零个或多个(一个?)站点,每个站点都是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个站点都有零个或多个建筑物,每个建筑物都在一个站点上,即外键 Building.SiteId
所指的站点。
最后:每个客户/站点/建筑物都只有一个地址,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指的地址。
您还有一个string searchText
。
您需要至少具有以下一项的所有客户的 ID:
类似于 searchText 的 CustomerName 至少有一个 SiteName 类似于 searchText 至少有一个 BuildingName 类似于 searchText 像 searchText 这样的 PhysicalAddress 他所有站点中的至少一个 SiteAddress 类似于 searchText 他的所有建筑物中至少有一个 BuildingAddress 类似于 searchText。我的建议是,从每个客户那里获取他的 ID,以及包含以下字符串的序列:
客户的姓名 他的实际地址 他所有站点和建筑物的名称 他所有站点和建筑物的地址结果是 [CustomerId,字符串序列] 的序列。您只想保留那些 CustomerId,其中“字符串序列”中至少有一个字符串类似于 searchText。
创建[CustomerId, sequence of strings] 组合并不难。尝试实现“like searchText”时会遇到问题。
让我们首先创建组合。
当您有“项目及其子项目”并且您想将它们视为一个项目序列时,请考虑使用Queryable.SelectMany 的重载之一。
var result = dbContext.Customers.SelectMany(customer => customer.Sites,
// parameter resultSelector: take every Customer with its Site to create one new:
(customer, sitesOfThisCustomer) => new
Id = customer.Id,
// the searchTexts: the customer name, his physical address
// the names and address of of all his Sites
// and the names and addresses of all the building of each side (inner SelectMany)
SearchTexts = new string[] customer.CustomerName, customer.PhysicalAddress
.Concat (sitesOfThisCustomer.SelectMany(site => site.Buildings,
(site, buildingsOfThisSite) => new string[] site.SiteName, site.SiteAddress
.Concat(buildingsOfThisSite.SelectMany(building => new string[]
building.BuildingName, building.BuildingAddress)));
我不确定new string[] ...
是否与 IQueryable 一起使用,如果没有,请考虑另一种方法来制作具有名称和地址的可枚举序列(Enumerable.Repeat?重复计数为 1?)
因此,现在您拥有每位客户的 ID 以及客户、他的站点和这些站点上的建筑物的名称和地址的大序列。您所要做的就是为Like searchText
添加.Where
。据我所知,标准 LINQ 没有这个,但也许你可以这样做:
.Where(customeWithSearchTexts => customerWithSearchTexts.SearchTexts
.Any(text => text.StartsWith(searchText));
在上面的解决方案中,我使用了您在实体框架中看到的virtual ICollection<...>
。如果你不能使用它,因为你的班级没有这个,你必须自己做组加入:
var result = customers.SelectMany(
// the Sites of this customre
customer => dbContext.Sites.Where(site.CustomerId == customer.Id),
// resultSelector:
(customer, sitesOfThisCustomer) => ...
// inner selectmany
site.SelectMany(dbContext.Buildings.Where(building.SiteId == site.Id),
...
【讨论】:
以上是关于具有多个连接条件的实体框架查询的主要内容,如果未能解决你的问题,请参考以下文章