SQL差异EF 6与EF Core

时间:2018-01-10 12:04:22

标签: c# sql sql-server entity-framework entity-framework-core

我正在将软件从EF 6迁移到EF Core。在测试过程中,我注意到Linq的解释方式存在差异。

My Linq

app.Deputies
   .Include(d => d.User)
   .Where(d => d.User == null)
   .ToList()

在EF 6中,它会产生一个查询(为了阅读而简化),如此

SELECT
  d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE u.Id IS NULL

在EF EF中,SQL看起来像这样

SELECT
  d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE d.UserId IS NULL

即使我做.Where(d => d.User.Id == null)也不会更改生成的查询。

EF 6的配置如下所示:

.HasOptional(d => d.User).WithMany().HasForeignKey(d => d.UserId);

EF Core的配置如下所示:

.HasOne(d => d.User).WithMany().HasForeignKey(d => d.UserId);

我是否遗漏了配置或任何想法中的内容,如何在EF 6中实现相同的SQL?

(我正在使用SQL Server)

编辑:数据库上的副用户和用户之间没有FK。 (仅限于模型中)

2 个答案:

答案 0 :(得分:3)

这两个查询

SELECT
  d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE u.Id IS NULL

SELECT
  d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE d.UserId IS NULL

如果副手在UserId上有外键,则语义相同。

查询之间的唯一区别是Vice具有非null UserId,但User表中不存在UserId。如果您在副手上有外键,那就不会发生。

因此,EF在这两种情况下的代码生成都是正确的。 EF Core的查询更好,因为可以在连接之前评估过滤器。

答案 1 :(得分:1)

(将我的评论转化为答案)

这是一个有趣的例子,说明实施中看似无辜的变化可能会产生意想不到的副作用。

EF6过滤联接右侧的联接:

SELECT d.*
FROM Deputy d LEFT OUTER JOIN User u 
ON          d.UserId =             u.Id
WHERE                              u.Id IS NULL

左侧的EF核心过滤器:

SELECT d.*
FROM Deputy d LEFT OUTER JOIN User u 
ON          d.UserId =             u.Id
WHERE       d.UserId IS NULL

SQL查询优化器并不疯狂,它发现第二个查询可以简化为:

SELECT d.*
FROM Deputy
WHERE d.UserId IS NULL

查询2和3的查询计划是相同的:只有索引扫描,而查询1包含一个嵌套循环来组合代理和用户结果。

因此,在User.IdDeputy.UserId之间存在外键约束的正常情况下,EF核心实现优于前者。但在你的情况下,没有FK。因此Deputee可能UserIdUser不匹配,并且它们被第二个查询过滤掉,而不是第一个查询,而LINQ查询是相同的。

差异可能非常重要,因此通常我们应该从EF-core中的这种改进的查询生成中受益(假设它是故意的)。但是,我们必须面对它,EF6版本是LINQ查询表达语义的更好的翻译。

您可以通过显式编写外部联接来解决此问题:

from d in db.Deputees
join u in db.Users on d.UserId equals u.Id into ug
from u in ug.DefaultIfEmpty() // LINQ eqivalent of outer join
where u == null
select d

...过滤u.Id或使用Any

db.Deputees.Where(d => !db.Users.Any(u => u.Id == d.UserId))

...转换为NOT EXISTS