已启用NRT,EF Core为何能为非可空属性赋值NULL?
我已启用NRT(可为空引用类型),定义了如下模型:
public class Blog { public int Id { get; set; } public string Url { get; set; } public Header Header { get; set; } } public class Header { public int Id { get; set; } public string Title { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }
根据EF Core官方文档,启用NRT时框架会遵循规则设置数据库列的可空性。但实际情况是,EF Core完全忽略了Blog类里非可空的Header属性——我甚至能创建未关联任何Header的Blog实例,此时Header会被设为NULL。更麻烦的是,编译器认为该属性不可为空,但运行时它实际是NULL。
生成的迁移代码如下:
migrationBuilder.CreateTable( name: "Blogs", columns: table => new { Id = table.Column<int>(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), Url = table.Column<string>(type: "TEXT", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Blogs", x => x.Id); }); migrationBuilder.CreateTable( name: "Headers", columns: table => new { Id = table.Column<int>(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), Title = table.Column<string>(type: "TEXT", nullable: false), BlogId = table.Column<int>(type: "INTEGER", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Headers", x => x.Id); table.ForeignKey( name: "FK_Headers_Blogs_BlogId", column: x => x.BlogId, principalTable: "Blogs", principalColumn: "Id", onDelete: ReferentialAction.Cascade); });
请问为何EF Core能够为非可空属性赋值NULL?
这是因为你定义的是导航属性,而非普通实体属性,EF Core对导航属性的NRT处理逻辑和普通属性存在差异:
导航属性的可空性定位:EF Core不会因为导航属性标记为非可空(
Header而非Header?)就强制数据库中存在关联记录。导航属性的可空性主要用于编译器静态分析,EF Core本身不会在插入、查询操作时自动验证或强制该属性非空。外键约束的方向限制:你的模型中
Header包含BlogId外键,这是依赖方(Header)指向主体方(Blog)的外键配置。EF Core会在Headers表中把BlogId设为非空(对应迁移代码里BlogId = table.Column<int>(nullable: false)),确保每个Header必须关联一个Blog;但反过来,Blog表没有对应指向Header的外键列,数据库层面没有约束要求Blog必须关联Header。实体初始化与加载逻辑:当查询未关联
Header的Blog时,EF Core不会自动创建Header实例,导航属性会保持NULL。如果未启用延迟加载,编译器会因NRT标记认为属性非空,但运行时实际值为NULL,这就导致了编译时和运行时的不一致。
如果想要强制Blog必须关联Header,需要将关系配置为一对一且主体方依赖于依赖方,或者在Blog类中显式添加指向Header的外键并标记为非可空,同时通过Fluent API或数据注解配置关系的必填约束。
内容的提问来源于stack exchange,提问作者noobie

