c# - Entity Framework : Any(. .)-从 EF5 升级到 EF6 后使用 == 过滤匹配 NULL

标签 c# asp.net-mvc entity-framework-6

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

顺便说一句,我也按照这里的步骤操作:https://learn.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);
    }
    ...
}

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

要点是,如果提供了 ExternalCourseNumber,则任何现有类(class)都不能存在,但如果未提供,我不想抛出验证错误。

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

即在我看来:

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 为空的类(class)。但是为什么/什么时候改变了?为什么 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中是undefined,查询什么都不返回。

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

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

在 EF6 中,此设置已作为 DbContext.Configuration.UseDatabaseNullSemantics 移至首选 DbContext,因此,实际上,与 UseCSharpNullComparisonBehavior 相反.重大更改是默认值也是 false,这可能是因为 C# 语义被认为是首选行为,因为这是大多数 EF 开发人员所期望的。

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

关于c# - Entity Framework : Any(. .)-从 EF5 升级到 EF6 后使用 == 过滤匹配 NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68488666/

相关文章:

javascript - 隐藏 KendoUI 下拉列表

visual-studio - 如何在 Visual Studio 服务器资源管理器中连接到 LocalDB?

c# - 这些性能数字 : Arrays vs Lists C# 背后的基本原理

c# - 以下代码的输出 : Why would it return rainyday[1] = Sunday and not friday?

c# - 使用泛型实现显式接口(interface)成员

sql-server - ASP.NET Core/Entity Framework - 使用多对多关系时,HTTP Post 不会在联接表中创建实体

c# - 没有可用的匹配绑定(bind),并且该类型不可自绑定(bind)。激活 IProductsRepository 时出错

c# - modelBuilder.Configurations.Add 和 modelBuilder.Entity onModelCreating

c# - 无法创建的实例,因为 Type.ContainsGenericParameters 为真

c# - 如果我不打开 SqlConnection 实例会怎样?