You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

迁移.NET Framework MVC到ASP.NET Core 6.0遇Identity用户声明错误

问题:ASP.NET Core 6.0迁移中的Identity实体类型不兼容错误

System.InvalidOperationException: 'The relationship from 'IdentityUserClaim' to 'User' with foreign key properties {'UserId' : string} cannot target the primary key {'Id' : int} because it is not compatible. Configure a principal key or a set of foreign key properties with compatible types for this relationship.'

错误原因

自定义User实体继承了默认主键为string的IdentityUser,但手动声明了int类型的Id属性,导致User主键类型变为int;而AspNetUserClaim等关联实体的UserId仍配置为string类型,两者类型不匹配,引发外键关联错误。

相关代码

DbContext

public class DbContext : IdentityDbContext<User>
{
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AspNetRole>(entity =>
            {
                entity.HasIndex(e => e.Name, "RoleNameIndex")
                    .IsUnique()
                    .HasFillFactor(80);

                entity.Property(e => e.Id).HasMaxLength(128);

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(256);
            });

            modelBuilder.Entity<AspNetUser>(entity =>
            {
                entity.HasIndex(e => e.UserName, "UserNameIndex")
                    .IsUnique()
                    .HasFillFactor(80);

                entity.Property(e => e.Id).HasMaxLength(128);

                entity.Property(e => e.Email).HasMaxLength(256);

                entity.Property(e => e.LockoutEndDateUtc).HasColumnType("datetime");

                entity.Property(e => e.UserName)
                    .IsRequired()
                    .HasMaxLength(256);

                entity.HasMany(d => d.Roles)
                    .WithMany(p => p.Users)
                    .UsingEntity<Dictionary<string, object>>(
                        "AspNetUserRole",
                        l => l.HasOne<AspNetRole>().WithMany().HasForeignKey("RoleId").HasConstraintName("FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId"),
                        r => r.HasOne<AspNetUser>().WithMany().HasForeignKey("UserId").HasConstraintName("FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId"),
                        j =>
                        {
                            j.HasKey("UserId", "RoleId").HasName("PK_dbo.AspNetUserRoles");

                            j.ToTable("AspNetUserRoles");

                            j.HasIndex(new[] { "RoleId" }, "IX_RoleId").HasFillFactor(80);

                            j.HasIndex(new[] { "UserId" }, "IX_UserId").HasFillFactor(80);

                            j.IndexerProperty<string>("UserId").HasMaxLength(128);

                            j.IndexerProperty<string>("RoleId").HasMaxLength(128);
                        });
            });

            modelBuilder.Entity<AspNetUserClaim>(entity =>
            {
                entity.HasIndex(e => e.UserId, "IX_UserId")
                    .HasFillFactor(80);

                entity.Property(e => e.UserId)
                    .IsRequired()
                    .HasMaxLength(128);

                entity.HasOne(d => d.User)
                    .WithMany(p => p.AspNetUserClaims)
                    .HasForeignKey(d => d.UserId)
                    .HasConstraintName("FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId");
            });

            modelBuilder.Entity<AspNetUserLogin>(entity =>
            {
                entity.HasKey(e => new { e.LoginProvider, e.ProviderKey, e.UserId })
                    .HasName("PK_dbo.AspNetUserLogins");

                entity.HasIndex(e => e.UserId, "IX_UserId")
                    .HasFillFactor(80);

                entity.Property(e => e.LoginProvider).HasMaxLength(128);

                entity.Property(e => e.ProviderKey).HasMaxLength(128);

                entity.Property(e => e.UserId).HasMaxLength(128);

                entity.HasOne(d => d.User)
                    .WithMany(p => p.AspNetUserLogins)
                    .HasForeignKey(d => d.UserId)
                    .HasConstraintName("FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId");
            });

            modelBuilder.Entity<User>(entity =>
            {
                entity.ToTable("User");
            });
        }
}

User实体

public class User : IdentityUser
{
    public int Id { get; set; }
    public string Username { get; set; }
}

已尝试的修改

User实体修改

public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; } = new HashSet<AspNetUserClaim>();

DbContext的OnModelCreating修改

entity.HasMany(x => x.AspNetUserClaims)
    .WithOne()
    .HasForeignKey(x => x.UserId)
    .IsRequired()
    .OnDelete(DeleteBehavior.Cascade);

解决方案

1. 修正User实体的继承关系

继承IdentityUser<int>指定主键类型为int,无需手动声明Id属性(基类已包含):

public class User : IdentityUser<int>
{
    public string Username { get; set; }
    public virtual ICollection<AspNetUserClaim> AspNetUserClaims { get; set; } = new HashSet<AspNetUserClaim>();
}

2. 修正DbContext的继承与配置

使用IdentityDbContext<User, IdentityRole<int>, int>匹配主键类型,并更新所有关联实体的主键/外键类型配置:

public class YourDbContext : IdentityDbContext<User, IdentityRole<int>, int>
{
    // 构造函数(根据依赖注入方式调整)
    public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 先调用基类方法确保Identity默认配置生效
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<AspNetRole>(entity =>
        {
            entity.HasIndex(e => e.Name, "RoleNameIndex")
                .IsUnique()
                .HasFillFactor(80);

            // 移除string类型的Id长度配置,改为int类型主键
            // entity.Property(e => e.Id).HasMaxLength(128);

            entity.Property(e => e.Name)
                .IsRequired()
                .HasMaxLength(256);
        });

        modelBuilder.Entity<AspNetUser>(entity =>
        {
            entity.HasIndex(e => e.UserName, "UserNameIndex")
                .IsUnique()
                .HasFillFactor(80);

            // 移除string类型的Id长度配置
            // entity.Property(e => e.Id).HasMaxLength(128);

            entity.Property(e => e.Email).HasMaxLength(256);
            entity.Property(e => e.LockoutEndDateUtc).HasColumnType("datetime");
            entity.Property(e => e.UserName)
                .IsRequired()
                .HasMaxLength(256);

            entity.HasMany(d => d.Roles)
                .WithMany(p => p.Users)
                .UsingEntity<Dictionary<string, object>>(
                    "AspNetUserRole",
                    l => l.HasOne<AspNetRole>().WithMany().HasForeignKey("RoleId").HasConstraintName("FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId"),
                    r => r.HasOne<AspNetUser>().WithMany().HasForeignKey("UserId").HasConstraintName("FK_dbo.AspNetUserRoles_dbo.AspNetUsers_UserId"),
                    j =>
                    {
                        j.HasKey("UserId", "RoleId").HasName("PK_dbo.AspNetUserRoles");
                        j.ToTable("AspNetUserRoles");
                        j.HasIndex(new[] { "RoleId" }, "IX_RoleId").HasFillFactor(80);
                        j.HasIndex(new[] { "UserId" }, "IX_UserId").HasFillFactor(80);

                        // 改为int类型的外键配置
                        j.IndexerProperty<int>("UserId");
                        j.IndexerProperty<int>("RoleId");
                    });
        });

        modelBuilder.Entity<AspNetUserClaim>(entity =>
        {
            entity.HasIndex(e => e.UserId, "IX_UserId")
                .HasFillFactor(80);

            // 移除string类型的UserId长度配置,改为int类型
            entity.Property(e => e.UserId)
                .IsRequired();

            entity.HasOne(d => d.User)
                .WithMany(p => p.AspNetUserClaims)
                .HasForeignKey(d => d.UserId)
                .HasConstraintName("FK_dbo.AspNetUserClaims_dbo.AspNetUsers_UserId");
        });

        modelBuilder.Entity<AspNetUserLogin>(entity =>
        {
            entity.HasKey(e => new { e.LoginProvider, e.ProviderKey, e.UserId })
                .HasName("PK_dbo.AspNetUserLogins");

            entity.HasIndex(e => e.UserId, "IX_UserId")
                .HasFillFactor(80);

            entity.Property(e => e.LoginProvider).HasMaxLength(128);
            entity.Property(e => e.ProviderKey).HasMaxLength(128);

            // 改为int类型的UserId
            entity.Property(e => e.UserId)
                .IsRequired();

            entity.HasOne(d => d.User)
                .WithMany(p => p.AspNetUserLogins)
                .HasForeignKey(d => d.UserId)
                .HasConstraintName("FK_dbo.AspNetUserLogins_dbo.AspNetUsers_UserId");
        });

        modelBuilder.Entity<User>(entity =>
        {
            entity.ToTable("User");
        });
    }
}

3. 修正AspNetUserClaim等关联实体的UserId类型

确保AspNetUserClaim的UserId为int类型:

public class AspNetUserClaim
{
    // 其他属性保持不变
    public int UserId { get; set; }
    public User User { get; set; }
}

通过以上修改,统一所有Identity相关实体的主键与外键类型为int,即可解决类型不兼容的关联错误。

内容的提问来源于stack exchange,提问作者Bad Dub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 12:12:24