以下是一个例子:
假设我有3张桌子,国家,人口和城市。城市记录具有标识国家的不可空的外键字段。人员记录中有一个可以为空的外键字段标识一个国家/地区 - 它们可能来自不再存在的东欧国家。
我想根据人员列表和城市列表创建国家/地区的组合列表:
var people = dbContext.People.Where(...);
var cities = dbContext.Cities.Where(...);
var countries = cities.Select(c=>c.Country);
countries = countries.Union(people.Select(p=>p.Country));
问题来自最后一行。由于并非所有人员记录都有匹配的国家/地区记录,因此LINQ(正确)创建了一个查询,以确保每个无国籍人员都会有一行填充空值。使用调试器,可以通过创建entra虚拟列调用“[test]”
来完成SELECT [t2].[test], [t2].[CountryID], [t2].[Name]
FROM [dbo].[People] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[CountryID], [t1].[Name]
FROM [dbo].[Countries] AS [t1]
) AS [t2] ON [t2].[CountryID] = [t0].[CountryID]
虽然在代码端静默删除了额外的列[test](结果被标识为IQueryable),但在SQL端它绝对是存在的,并且当我将其与一个联合时导致查询被拒绝正常的国家选择声明。
在最后一种情况下,我不想要或不需要额外的虚拟行 - 在完整的程序中,我已经包含people.Where(p=>p.CountryID != null).Select(p=>p.Country)
以及尝试p=>Country != null
。
然而,Linq没有意识到这会阻止空行,因此仍会插入测试列。由于测试列是不可见的,我没有明显的方法将其从报告为IQueryable对象的内容中“删除”。最终结果是我的UNION结构的运行时错误具有不相等的列数。
如何强制INNER JOIN打开我的可空关系,或者通过排除不可见的测试列使联合工作正常?
答案 0 :(得分:1)
我知道理想情况下你会使用映射中的关系,但这可能是一个很好的解决方法。
var people = dbContext.People.Where(...);
var cities = dbContext.Cities.Where(...);
var countryIds =
cities
.Select(c => c.CountryID)
.Union(people
.Select(p => p.CountryID)
.Where(cID => cID.HasValue)
.Select(cID => cID.Value));
var countries = dbContext.Countries
.Where(c => countryIds.Contains(c.CountryID));