影响 UNION 操作的 Linq 和可为 null 的键关系

发布于 2024-08-03 18:26:49 字数 1200 浏览 7 评论 0原文

下面是一个例子:

假设我有 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 正在(正确地)创建一个查询,以确保每个无国籍人员都有一行填充空值。使用调试器,这似乎是通过创建一个内部虚拟列调用“[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,或者通过排除不可见的测试列来使联合按我想要的方式工作?

Here is an example:

Lets say I have 3 tables, Countries, People and Cities. City records have a non-nullable foreign key field identifying a country. People records have a nullable foreign key field identifying a country - they may be from an Eastern European country that no longer exists.

I want to create a combined list of countries from a list of people and a list of cities:

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));

The problem comes from that last line. Since not all people records have a matching country record, LINQ is (correctly) creating a query that ensures there will be a row filled with nulls for every countryless person. With a debugger it appears this is done by creating an entra dummy column call "[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]

While that extra column [test] is silently removed (the result is identified as IQueryable) on the code side, on the SQL side it is most definitely there, and is resulting in the query being rejected when I union it with a normal Country selection statement.

In the final case I don't want or need the extra dummy rows - in the full program I've already included people.Where(p=>p.CountryID != null).Select(p=>p.Country) as well as trying p=>Country != null.

However Linq does not recognize that this will prevent the null rows and so still inserts the test column. as the test column is invisible, I have no obvious way of "removing" it from what is otherwise reported as an IQueryable object. The end result is the run time error about my UNION construct having an unequal number of columns.

How can I force an INNER JOIN on on my nullable relationship, or otherwise make the union work as I want to by excluding the invisible test column?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

狼亦尘 2024-08-10 18:26:49

我知道理想情况下您会使用映射中的关系,但这可能是一个很好的解决方法。

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));

I know that ideally you'd use the relationships in the mapping, but this might be a good work-around.

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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文