繁体   English   中英

实体框架:从 EF5 升级到 EF6 后,使用 == 的 Any(..) 过滤匹配 NULL

[英]Entity Framework: Any(..)-filtering with == matches NULL after upgrading from EF5 to EF6

作为将我的 ASP.NET MVC 项目升级到面向 .NET Framework v4.8 和 ASP.NET MVC v5.2.7 的一部分,Entity Framework 也从 v5.0.0 升级到了 v6.4.4; 在那之后,我注意到使用Any()构造的一个奇怪的变化。

顺便说一句,我也按照这里的步骤操作: https : //docs.microsoft.com/en-us/ef/ef6/what-is-new/upgrading-to-ef6

我的项目中有这段代码(升级前后代码相同)

[HttpPost]
public ActionResult Create(Course course)
{
    ...
    if (context.Courses.Any(c => c.ExternalCourseNumber == course.ExternalCourseNumber))
    {
        throw new Exception(Resources.Global.ExternalCourseIdAlreadyExists);
    }
    ...
}

这是一种验证,是创建新Course Course对象是传递给 Create 方法的模型,并且字段ExternalCourseNumber在网络表单上留空,因此在 Visual Studio 中调试时course.ExternalCourseNumber的值为 null。

关键是,如果提供了ExternalCourseNumber ,它不能存在于任何现有课程中,但是当它没有被提供时,我不想抛出验证错误。

在升级之前,当ExternalCourseNumber为空/空时,此验证不会引发验证错误。 升级后,我的代码抛出验证错误。

即在我看来:

Courses.Any(c => c.ExternalCourseNumber == course.ExternalCourseNumber) 

升级后与 null 相比时行为发生了变化……这让我感到困惑?!?

我知道如何通过测试空值来解决问题,但行为的变化让我担心,因为它也可能影响其他功能,所以我想了解发生了什么?

== 运算符是否也开始匹配 NULL 值,还是我遗漏了什么?

=== 更新:从 EF6 生成的 SQL =====

我设法使用 EF6 日志框架来查看 SQL:

SELECT 
    CASE WHEN ( EXISTS (SELECT 
        1 AS [C1]
        FROM [dbo].[Courses] AS [Extent1]
        WHERE ([Extent1].[ExternalCourseNumber] = @p__linq__0) OR (([Extent1].[ExternalCourseNumber] IS NULL) AND (@p__linq__0 IS NULL))
    )) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1]
    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

...所以这解释了为什么查询将包括 ExternalCourseNumber 为空的课程。 但是为什么/什么时候改变了? 为什么 EF5 的行为方式不同? 这似乎是一个“重大变化”?

PS 我不知道如何查看 EF5 中生成的 SQL...有什么建议吗?

这与数据库空语义的重大变化有关。

在 EF6 中,像context.Courses.Any(c => c.ExternalCourseNumber == course.ExternalCourseNumber)这样的查询将转换为类似的内容:

WHERE ([Extent1].[ExternalCourseNumber] = @p__linq__0) 
    OR (([Extent1].[ExternalCourseNumber] IS NULL) AND (@p__linq__0 IS NULL))

(其中@p__linq__0是一个等于null的 (n)varchar 变量)。

在 EF5 中,这将类似于:

WHERE [Extent1].[ExternalCourseNumber] = @p__linq__0

第二个查询将nullnull进行比较,后者在 SQL 中是未定义的,并且查询不返回任何内容。

在 EF6 中,通过使比较语义等于 C# 中的语义来解决此问题,其中null == null返回true 这需要一个额外的OR谓词。 第一个查询 (EF6) 返回ExternalCourseNumbernull课程。

令人惊讶的是,这种重大变化没有明确记录。 在 EF5 中, ObjectContext已经有一个设置ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior通过它可以改变行为。 默认为false 将其设置为true会将 SQL 转换为第一个查询的转换。

在EF6该设置被移到了优选DbContextDbContext.Configuration.UseDatabaseNullSemantics ,所以,实际上的相对UseCSharpNullComparisonBehavior 重大变化是默认值也是false ,可能是因为 C# 语义被认为是首选行为,因为它是大多数 EF 开发人员所期望的。

在您的情况下,我不会更改UseDatabaseNullSemantics ,而是简单地添加额外的检查,如果course.ExternalCourseNumber不等于null ,因为它使代码更不言自明。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM