.NET Core 3.0 MySQL迁移添加外键约束时出错求助
你在给CatalogProductBid关联ApplicationUser(继承自IdentityUser)时遇到的errno:150错误,本质是MySQL认为外键约束的定义不符合规则。结合你的代码和迁移脚本,我整理了几个最可能的原因及对应的解决思路:
1. 字段数据类型/字符集/排序规则不匹配
IdentityUser默认的Id字段是string类型,在MySQL中会被映射为NVARCHAR(450),并且通常使用utf8mb4字符集和对应的排序规则。而你的迁移脚本中添加的UserId字段没有明确指定字符集,可能和AspNetUsers.Id的字符集/排序规则不一致,这会导致MySQL无法创建外键。
解决方法:
修改迁移脚本,给UserId字段指定和AspNetUsers.Id完全一致的字符集和排序规则。比如:
migrationBuilder.AddColumn<string>( name: "UserId", table: "CatalogProductBids", nullable: false, defaultValue: "", annotations: new Dictionary<string, object> { ["MySql:CharSet"] = "utf8mb4", ["MySql:Collation"] = "utf8mb4_unicode_ci" });
你可以先执行SHOW CREATE TABLE AspNetUsers;命令查看AspNetUsers表的Id字段属性,确保UserId和它完全一致。
2. 现有数据不符合外键约束
你的迁移脚本给UserId设置了defaultValue: "",但AspNetUsers表中不存在Id为空字符串的用户。如果CatalogProductBids表中已经有数据,这些数据的UserId会被设为空字符串,而外键要求所有UserId必须在AspNetUsers.Id中存在,这就会触发错误。
解决方法:
- 如果
CatalogProductBids表还没有数据:可以直接移除defaultValue: "",或者把默认值改成一个已存在的用户Id。 - 如果表中已有数据:先添加允许为空的
UserId字段,更新所有数据的UserId为有效用户Id,再修改字段为非空并添加外键。修改后的迁移脚本示例:
protected override void Up(MigrationBuilder migrationBuilder) { // 先添加可空的UserId migrationBuilder.AddColumn<string>( name: "UserId", table: "CatalogProductBids", nullable: true, annotations: new Dictionary<string, object> { ["MySql:CharSet"] = "utf8mb4", ["MySql:Collation"] = "utf8mb4_unicode_ci" }); // 更新现有数据为有效用户Id(替换成你实际存在的用户Id) migrationBuilder.Sql("UPDATE CatalogProductBids SET UserId = 'your-valid-user-id' WHERE UserId IS NULL;"); // 修改为非空字段 migrationBuilder.AlterColumn<string>( name: "UserId", table: "CatalogProductBids", nullable: false, annotations: new Dictionary<string, object> { ["MySql:CharSet"] = "utf8mb4", ["MySql:Collation"] = "utf8mb4_unicode_ci" }); migrationBuilder.CreateIndex( name: "IX_CatalogProductBids_UserId", table: "CatalogProductBids", column: "UserId"); migrationBuilder.AddForeignKey( name: "FK_CatalogProductBids_AspNetUsers_UserId", table: "CatalogProductBids", column: "UserId", principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }
3. 表引擎不支持外键
MySQL的MyISAM引擎不支持外键约束,而InnoDB才支持。如果CatalogProductBids表使用的是MyISAM引擎,就会导致外键创建失败。Identity默认创建的AspNetUsers表是InnoDB,但你自己的表可能不是。
解决方法:
在迁移脚本中确保CatalogProductBids表使用InnoDB引擎:
migrationBuilder.AlterTable("CatalogProductBids") .Annotation("MySql:Engine", "InnoDB");
把这段代码添加到Up方法的开头即可。
4. 主表字段不是主键/没有索引
虽然IdentityUser的Id默认是主键(自动带有索引),但如果你的AspNetUsers表被手动修改过,导致Id不是主键或没有索引,也会触发这个错误。不过这种情况比较少见,你可以用SHOW CREATE TABLE AspNetUsers;命令确认Id是主键。
先从数据类型/字符集不匹配和现有数据约束这两个最常见的原因排查,应该能解决问题。
内容的提问来源于stack exchange,提问作者Roel

