Entity Framework 具有多个连接条件的查询
Entity Framework Query with multiple join conditions
已编辑
我有表 Customers
、Sites
、Buildings
和 Addresses
.
每个客户都有零个或多个(一个?)站点,每个站点恰好是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个站点都有零个或多个建筑物,每个建筑物恰好在一个站点上,即外键Building.SiteId
所指的站点。
最后:每个customer/site/building只有一个地址,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指向的地址。
我也有一个string searchText
我想要所有具有至少以下一项的客户的 ID:
- a
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%'
控制器 class 在编写 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
};
}
在结果查询中我只想让customer Id满足以上条件。在引入 Addresses
实体之前,linq 查询工作正常。面对写多个on条件,LinqPad报错
The type of one of the expression in the join clause is incorrect. Type reference failed in the call to GroupJoin
我是 EF 和 linq 的新手 - 只是尝试和理解它。
感谢任何宝贵的意见和回答。
所以你有 Customers
、Sites
、Buildings
和 Addresses
的表格。
每个客户都有零个或多个(一个?)站点,每个站点恰好是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个Site都有零个或多个建筑物,每个建筑物恰好在一个Site上,即外键Building.SiteId
所指的Site。
最后:每个Customer/Site/Building都有一个Address,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指的Address。
你还有一个string searchText
.
您需要具有至少以下一项的所有客户的 ID:
- 类似于 searchText 的 CustomerName
- 至少一个类似于 searchText
的站点名称
- 至少一个 BuildingName 类似于 searchText
- 像搜索文本这样的物理地址
- 他的所有站点中至少有一个站点地址类似于 searchText
- 他所有建筑物中至少有一个类似于 searchText 的 BuildingAddress。
我的建议是,从每个客户那里获取他的 ID,以及包含以下字符串的序列:
- 客户姓名
- 他的实际地址
- 他所有站点和建筑物的名称
- 他所有站点和建筑物的地址
结果是 [CustomerId,字符串序列] 的序列。您只想保留那些 CustomerId,其中“字符串序列”中至少有一个字符串类似于 searchText。
创建 [CustomerId, 字符串序列] 组合并不困难。尝试实现“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? with repeat数 1?)
所以现在您拥有每个客户的 ID 和一大串客户的姓名和地址、他的站点以及这些站点上的建筑物。您所要做的就是为 Like searchText
添加一个 .Where
。据我所知,标准 LINQ 没有这个,但也许你可以这样做:
.Where(customeWithSearchTexts => customerWithSearchTexts.SearchTexts
.Any(text => text.StartsWith(searchText));
在上面的解决方案中,我使用了您在 entity framework 中看到的 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),
...
如果您看到 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 查询中删除连接。
已编辑
我有表 Customers
、Sites
、Buildings
和 Addresses
.
每个客户都有零个或多个(一个?)站点,每个站点恰好是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个站点都有零个或多个建筑物,每个建筑物恰好在一个站点上,即外键Building.SiteId
所指的站点。
最后:每个customer/site/building只有一个地址,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指向的地址。
我也有一个string searchText
我想要所有具有至少以下一项的客户的 ID:
- a
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%'
控制器 class 在编写 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
};
}
在结果查询中我只想让customer Id满足以上条件。在引入 Addresses
实体之前,linq 查询工作正常。面对写多个on条件,LinqPad报错
The type of one of the expression in the join clause is incorrect. Type reference failed in the call to GroupJoin
我是 EF 和 linq 的新手 - 只是尝试和理解它。
感谢任何宝贵的意见和回答。
所以你有 Customers
、Sites
、Buildings
和 Addresses
的表格。
每个客户都有零个或多个(一个?)站点,每个站点恰好是一个客户的站点,即外键 Site.CustomerId
所指的站点。
同样,每个Site都有零个或多个建筑物,每个建筑物恰好在一个Site上,即外键Building.SiteId
所指的Site。
最后:每个Customer/Site/Building都有一个Address,即外键Customer.CustomerPhysicalAddressId
、Site.AddressId
、Building.BuildingAddressId
所指的Address。
你还有一个string searchText
.
您需要具有至少以下一项的所有客户的 ID:
- 类似于 searchText 的 CustomerName
- 至少一个类似于 searchText 的站点名称
- 至少一个 BuildingName 类似于 searchText
- 像搜索文本这样的物理地址
- 他的所有站点中至少有一个站点地址类似于 searchText
- 他所有建筑物中至少有一个类似于 searchText 的 BuildingAddress。
我的建议是,从每个客户那里获取他的 ID,以及包含以下字符串的序列:
- 客户姓名
- 他的实际地址
- 他所有站点和建筑物的名称
- 他所有站点和建筑物的地址
结果是 [CustomerId,字符串序列] 的序列。您只想保留那些 CustomerId,其中“字符串序列”中至少有一个字符串类似于 searchText。
创建 [CustomerId, 字符串序列] 组合并不困难。尝试实现“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? with repeat数 1?)
所以现在您拥有每个客户的 ID 和一大串客户的姓名和地址、他的站点以及这些站点上的建筑物。您所要做的就是为 Like searchText
添加一个 .Where
。据我所知,标准 LINQ 没有这个,但也许你可以这样做:
.Where(customeWithSearchTexts => customerWithSearchTexts.SearchTexts
.Any(text => text.StartsWith(searchText));
在上面的解决方案中,我使用了您在 entity framework 中看到的 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),
...
如果您看到 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 查询中删除连接。