继承IdentityUser的ApplicationUser在EF更新数据库时的报错及低风险解决咨询
尝试在继承IdentityUser的ApplicationUser与Transactions模型间建立一对多关系,相关模型代码如下:
ApplicationUser 代码
public class ApplicationUser : IdentityUser { public ICollection<Transactions> Transactions { get; set; } }
Transactions 代码
public class Transactions { public int Id { get; set; } public int PhoneId { get; set; } public Phone Phone { get; set; } public int CustomerId { get; set; } public Customers Customer { get; set; } public string UserId { get; set; } public ApplicationUser User { get; set; } public TransactionType TransactionType { get; set; } public DateTime TransactionDate { get; set; } public decimal TransactionAmount { get; set; } }
添加新迁移后,除创建Transactions表外,迁移文件还生成了修改AspNet系列系统表的操作:
migrationBuilder.AlterColumn<string>( name: "Name", table: "AspNetUserTokens", type: "nvarchar(128)", maxLength: 128, nullable: false, oldClrType: typeof(string), oldType: "nvarchar(450)"); migrationBuilder.AlterColumn<string>( name: "LoginProvider", table: "AspNetUserTokens", type: "nvarchar(128)", maxLength: 128, nullable: false, oldClrType: typeof(string), oldType: "nvarchar(450)"); migrationBuilder.AddColumn<string>( name: "Discriminator", table: "AspNetUsers", type: "nvarchar(max)", nullable: false, defaultValue: ""); migrationBuilder.AlterColumn<string>( name: "ProviderKey", table: "AspNetUserLogins", type: "nvarchar(128)", maxLength: 128, nullable: false, oldClrType: typeof(string), oldType: "nvarchar(450)"); migrationBuilder.AlterColumn<string>( name: "LoginProvider", table: "AspNetUserLogins", type: "nvarchar(128)", maxLength: 128, nullable: false, oldClrType: typeof(string), oldType: "nvarchar(450)");
执行Update-Database时出现错误:
Error Number:5074,State:1,Class:16
The object 'PK_AspNetUserTokens' is dependent on column 'Name'.
ALTER TABLE ALTER COLUMN Name failed because one or more objects access this column.
不想通过删除主键约束的方式解决,寻求低风险方案。
原因分析
报错是因为AspNetUserTokens表的主键由LoginProvider和Name列共同组成,EF Core生成的迁移尝试修改这两列的长度,但主键约束依赖这些列,直接修改会触发错误。这些修改操作并非建立ApplicationUser与Transactions关系所必需的,是EF Core对Identity默认配置的自动调整。
方案1:手动清理迁移文件(最直接)
打开生成的迁移文件,删除所有修改AspNetUserTokens和AspNetUserLogins列的代码块,只保留以下必要操作:
AspNetUsers表添加Discriminator列(继承IdentityUser必须)- 创建
Transactions表及关联外键的代码
修改后的迁移文件仅保留核心逻辑,示例如下:
public partial class AddTransactionsAndUserRelationship : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Discriminator", table: "AspNetUsers", type: "nvarchar(max)", nullable: false, defaultValue: ""); migrationBuilder.CreateTable( name: "Transactions", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), PhoneId = table.Column<int>(type: "int", nullable: false), CustomerId = table.Column<int>(type: "int", nullable: false), UserId = table.Column<string>(type: "nvarchar(450)", nullable: true), TransactionType = table.Column<int>(type: "int", nullable: false), TransactionDate = table.Column<DateTime>(type: "datetime2", nullable: false), TransactionAmount = table.Column<decimal>(type: "decimal(18,2)", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Transactions", x => x.Id); table.ForeignKey( name: "FK_Transactions_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Restrict); // 其他外键约束(Phone、Customer) }); migrationBuilder.CreateIndex( name: "IX_Transactions_UserId", table: "Transactions", column: "UserId"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Transactions"); migrationBuilder.DropColumn( name: "Discriminator", table: "AspNetUsers"); } }
清理完成后,重新执行Update-Database即可。
方案2:配置Identity列长度与数据库现有值匹配
在DbContext的OnModelCreating方法中,显式配置Identity系统表的列长度,与数据库中现有长度一致(即nvarchar(450)),这样EF Core就不会生成修改列的迁移操作。
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置AspNetUserTokens列长度,匹配现有数据库 modelBuilder.Entity<IdentityUserToken<string>>() .Property(t => t.LoginProvider) .HasMaxLength(450); modelBuilder.Entity<IdentityUserToken<string>>() .Property(t => t.Name) .HasMaxLength(450); // 配置AspNetUserLogins列长度 modelBuilder.Entity<IdentityUserLogin<string>>() .Property(l => l.LoginProvider) .HasMaxLength(450); modelBuilder.Entity<IdentityUserLogin<string>>() .Property(l => l.ProviderKey) .HasMaxLength(450); // 显式配置ApplicationUser与Transactions的一对多关系 modelBuilder.Entity<ApplicationUser>() .HasMany(u => u.Transactions) .WithOne(t => t.User) .HasForeignKey(t => t.UserId) .OnDelete(DeleteBehavior.Cascade); }
配置完成后,删除之前生成的迁移文件,重新执行Add-Migration生成新的迁移,再执行Update-Database。
方案3:生成SQL脚本并手动编辑
如果不想修改迁移文件,可先生成SQL迁移脚本,手动移除修改AspNetUserTokens和AspNetUserLogins列的语句,再执行脚本:
- 执行命令生成脚本:
- Package Manager Console:
Script-Migration - CLI:
dotnet ef migrations script
- Package Manager Console:
- 在生成的SQL脚本中,删除所有修改
AspNetUserTokens和AspNetUserLogins列的ALTER TABLE语句 - 在数据库管理工具(如SSMS)中执行修改后的脚本
内容的提问来源于stack exchange,提问作者Marko Mrđa

