世界快资讯:[学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误

博客园   2023-05-18 19:34:13

今天下午在排查一个EF问题时,遇到了个很隐蔽的坑,特此记录。

问题

使用ef执行Insert对象到某表时报错,此对象的Address为空:


(资料图片仅供参考)

不能将值 NULL 插入列 "Address",表 "dbo.xxx";列不允许有 Null 值。INSERT 失败。

检查数据库和迁移文件时发现Address这个字段被意外设置成nullable: false,而其它的字段却正常,按理来说对于string类型的属性,EFCore在codefirst模式下应该映射为可空类型。

代码也确认了实体中不包含[Required]注释,在任何地方也没有出现.IsRequired()的调用。

于是开始排查:手动创建一个空程序集,引用EFCore,从原项目拷贝EF设计时库、DbContext和各实体类,一顿操作后竟然发现在新的程序集中生成的迁移文件是符合预期的。令人费解,在多次比对代码之后,发现是.csproj文件中的这一行配置导致的

enable
原因分析

C# 8 引入了一项名为可为 null 引用类型 (NRT) 的新功能。官方文档该功能允许对引用类型进行批注,指示引用类型能否包含 null。

通过查看EF文档了解到,可为空引用类型通过以下方式影响 EF Core 的行为:

如果禁用可为空引用类型,则按约定将具有 .NET 引用类型的所有属性配置为可选 (例如 string ) 。如果启用了可为 null 的引用类型,则基于属性的 .NET 类型的 C# 为 Null 性来配置属性:string? 将配置为可选属性,但 string 将配置为必需属性。

换而言之,启用了该功能后,把原本《引用类型可为空》的这个传统约定,更改称为了《引用类型是否可为空,是通过?语法来表明的》,实体中string类型的属性在C#中作为引用类型,自然而然地受到了这个影响。

果然,在删除了这个功能后,string?的语法将不起作用

解决

关闭此功能,重新生成迁移,更新数据库,问题解决。

后记

语言特性会影响EF实体与表结构映射的约定,官方示例中对于string类型的处理方式也做了说明:

无NRT

public class CustomerWithoutNullableReferenceTypes{    public int Id { get; set; }    [Required] // Data annotations needed to configure as required    public string FirstName { get; set; }    [Required]    public string LastName { get; set; } // Data annotations needed to configure as required    public string MiddleName { get; set; } // Optional by convention}

有NRT

public class Customer{    public int Id { get; set; }    public string FirstName { get; set; } // Required by convention    public string LastName { get; set; } // Required by convention    public string? MiddleName { get; set; } // Optional by convention    // Note the following use of constructor binding, which avoids compiled warnings    // for uninitialized non-nullable properties.    public Customer(string firstName, string lastName, string? middleName = null)    {        FirstName = firstName;        LastName = lastName;        MiddleName = middleName;    }}

这两种模型的数据库映射是等价的。

之后应留意项目的"NRT"功能是否开启,在解决方案.csproj文件中用如下方式关闭

disable

留意实体类中是否有代码段被标识"NRT"功能开启

#nullable disable#nullable enable

从 .NET 6 开始,默认情况下会为新项目启用这些功能。原始项目是.NET 5.0升级而来的,所以项目文件中并不会包含Nullable相关的配置。

为了一行bug,好值得的一个下午呢

猜你喜欢

【道明证券:美国4月零售销售将强劲反弹1.0%】道明证券研报称,我们预计,继3月份环比下降0.6%之后,4月份美国零售销售额环比将强劲反弹1.0%。汽车和加油站的销售额可能是关键的推动因素。重要的是,得益于线上销售额似乎企稳,控制组(扣除汽车、汽油、建筑材料和食品服务)的销售额可能在2月和3月连续两次下滑后在4月有所回升。我们还预计,酒吧/餐厅的销售额将快速增长。_天天热讯

2023-05-14 22:11:32

[ 最近更新 ]