ASP Core添加一对一关系后EF Core迁移报错:SQLite非空约束失败
问题分析与解决方案
错误原因
- NOT NULL约束错误:SQLite在EF Core迁移过程中会通过临时表重写表结构,若原
ContentPosts表已有数据,添加可空RatingId列后立即设置外键,EF Core的临时表处理逻辑可能错误地将RatingId视为非空字段,触发约束异常。 - 外键约束错误:设置默认值
0L时,Ratings表为空,不存在Id=0的记录,违反外键引用规则,导致约束失败。 - 列未创建:迁移执行到外键步骤时失败回滚,导致之前的添加列操作未提交到数据库。
修复步骤
修改迁移的Up方法,调整操作顺序并处理现有数据,确保外键约束能正常创建:
protected override void Up(MigrationBuilder migrationBuilder) { // 1. 先创建Ratings表,避免外键引用时目标表不存在 migrationBuilder.CreateTable( name: "Ratings", columns: table => new { Id = table.Column<long>(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), Commends = table.Column<long>(type: "INTEGER", nullable: false), Views = table.Column<long>(type: "INTEGER", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Ratings", x => x.Id); }); // 2. 添加ContentPosts的RatingId可空列 migrationBuilder.AddColumn<long>( name: "RatingId", table: "ContentPosts", type: "INTEGER", nullable: true); // 3. 为现有ContentPosts生成对应的Ratings记录并关联(SQLite专用) migrationBuilder.Sql(@" -- 为每条ContentPosts创建初始Ratings数据 INSERT INTO Ratings (Commends, Views) SELECT 0, 0 FROM ContentPosts; -- 关联ContentPosts和对应的Ratings UPDATE ContentPosts SET RatingId = (SELECT Id FROM Ratings WHERE ROWID = ContentPosts.ROWID); "); // 4. 创建唯一索引(一对一关系必须添加,确保一个Rating仅对应一个ContentPost) migrationBuilder.CreateIndex( name: "IX_ContentPosts_RatingId", table: "ContentPosts", column: "RatingId", unique: true); // 5. 将RatingId设为非空(可选,若需要严格一对一) migrationBuilder.AlterColumn<long>( name: "RatingId", table: "ContentPosts", type: "INTEGER", nullable: false, oldClrType: typeof(long), oldType: "INTEGER", oldNullable: true); // 6. 添加外键约束,使用级联删除更符合一对一关系逻辑 migrationBuilder.AddForeignKey( name: "FK_ContentPosts_Ratings_RatingId", table: "ContentPosts", column: "RatingId", principalTable: "Ratings", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }
额外建议
确保EF Core模型的一对一关系配置正确,在DbContext的OnModelCreating方法中添加:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<ContentPost>() .HasOne(c => c.Rating) .WithOne(r => r.ContentPost) .HasForeignKey<ContentPost>(c => c.RatingId) .OnDelete(DeleteBehavior.Cascade); }
这样后续生成的迁移会自动符合一对一关系的规范,减少手动调整迁移代码的需求。
内容的提问来源于stack exchange,提问作者Epic Gamer
相关产品推荐
相关产品推荐

