已编辑
我有表格Customers
,Sites
,Buildings
和Addresses
。
每个客户都有零个或多个(一个?)站点,每个站点都是一个客户的站点,即外键Site.CustomerId
所指的站点。
类似地,每个站点都有零个或多个建筑物,每个建筑物都在一个站点上,即外键Building.SiteId
所指的站点。
最后:每个客户/站点/建筑物都只有一个地址,即外键Customer.CustomerPhysicalAddressId
,Site.AddressId
,Building.BuildingAddressId
所引用的地址。
我还有一个string searchText
我想要具有至少以下一项的所有客户的ID:
CustomerName
的{{1}} searchText
类似于SiteName
searchText
类似于BuildingName
searchText
的{{1}} PhysicalAddress
中至少有一个searchText
,例如SiteAddress
Sites
中至少有一个searchText
,例如BuildingAddress
对于上述要求,我有此SQL查询逻辑
Buildings
在编写linq查询时,控制器类中会出现问题。
我的Linq查询编写如下
searchText
在结果查询中,我只想让客户ID满足以上条件。在引入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查询可以正常工作。面对要写多个条件的情况,LinqPad显示错误
join子句中的表达式之一的类型不正确。调用GroupJoin
时类型引用失败
我是EF和linq的新手,只是尝试并理解它。
感谢您提出宝贵的意见和答案。
答案 0 :(得分:1)
因此,您有Customers
,Sites
,Buildings
和Addresses
表。
每个客户都有零个或多个(一个?)站点,每个站点都是一个客户的站点,即外键Site.CustomerId
所指的站点。
类似地,每个站点都有零个或多个建筑物,每个建筑物都在一个站点上,即外键Building.SiteId
所指的站点。
最后:每个客户/站点/建筑物都只有一个地址,即外键Customer.CustomerPhysicalAddressId
,Site.AddressId
,Building.BuildingAddressId
所引用的地址。
您还有一个string searchText
。
您希望所有具有以下至少一项的客户的ID:
我的建议是,从每个客户那里获得他的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?重复计数为1?)。
因此,现在您每个客户都有一个ID,以及客户,其站点以及这些站点上的建筑物的名称和地址的一个大序列。您要做的就是为.Where
添加一个Like searchText
。据我所知,标准的LINQ没有这个功能,但是也许您可以做类似的事情:
.Where(customeWithSearchTexts => customerWithSearchTexts.SearchTexts
.Any(text => text.StartsWith(searchText));
在上述解决方案中,我使用了在实体框架中看到的virtual ICollection<...>
。如果您不能使用它,因为您的班级没有这个,则必须自己进行groupjoin:
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),
...
答案 1 :(得分: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查询中删除联接。