C# SQL差异EF 6与EF核心
我正在将一个软件从EF 6迁移到EF Core。在测试期间,我注意到Linq的解释方式有所不同 我的林克C# SQL差异EF 6与EF核心,c#,sql,sql-server,entity-framework,entity-framework-core,C#,Sql,Sql Server,Entity Framework,Entity Framework Core,我正在将一个软件从EF 6迁移到EF Core。在测试期间,我注意到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 NU
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
SELECT
d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE d.UserId IS NULL
在efcore中,SQL如下所示
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
即使我执行了,其中(d=>d.User.Id==null)
也不会更改生成的查询
EF 6的配置如下所示:
.HasOptional(d => d.User).WithMany().HasForeignKey(d => d.UserId);
.HasOne(d => d.User).WithMany().HasForeignKey(d => d.UserId);
EF Core的配置如下所示:
.HasOptional(d => d.User).WithMany().HasForeignKey(d => d.UserId);
.HasOne(d => d.User).WithMany().HasForeignKey(d => d.UserId);
我是否在配置中遗漏了一些东西,或者有没有想到如何实现EF6中相同的SQL
(我正在使用SQL Server)
编辑:DB上代理和用户之间没有FK。(仅在模型中)这两个查询
SELECT
d.*
FROM Deputy d
LEFT JOIN User u ON u.Id = d.UserId
WHERE u.Id IS NULL
及
如果代理在UserId上有外键,则在语义上是相同的
查询之间的唯一区别是代理具有非空UserId,但该UserId在用户表中不存在。如果代理上有外键,就不会发生这种情况
所以EF在这两种情况下的代码生成都是正确的。EF Core的查询更好,因为可以在加入之前对过滤器进行评估 (将我的评论转化为答案)
这是一个有趣的例子,说明了实现中看似无辜的更改可能会产生意想不到的副作用
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.Id
和subsive.UserId
之间存在外键约束,EF核心实现优于前者。但在你的情况下,没有FK。因此Deputee
s可能具有与任何User
不匹配的UserId
s,并且它们被第二个查询过滤掉,而不是第一个查询,而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))
…翻译成一个
不存在
您发布的两个SQL查询是相同的。只是想知道,为什么您要包括检查null而不是直接检查外键?i、 eapp.depress.Where(d=>d.UserId==null).ToList()
核心版本更好,因为它不需要查询计划中的连接,所以为什么您想要EF6版本?我假设您有。重要的区别在于核心查询在subsive
上过滤,而不是在User
上过滤。这使得查询计划更加高效。好吧,我错过了那个。在这种情况下,EF商店模型和实际商店模型之间存在语义差异,您有责任解决它,例如,将其更改为Any
查询(其中不是Users.Any(u.Id==d.UserId)
),谢谢您的回答。我知道,如果UserId上有一个FK,它的行为将是相同的。但是DB上没有,只有模型上没有。我是否可以指示modelBuilder,仅在模型中的DB上不存在此关系?或者类似的东西?:)