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

.NET Core 6迁移外部登录报错:无法使用AspNetRoleClaims表

问题描述

我在.NET Core 3.1项目里实现了一套外部登录逻辑:通过查询字符串接收包含用户邮箱的令牌,验证令牌有效后完成用户登录。升级到.NET Core 6后,这套逻辑在3.1环境正常运行,但在6.0环境调用SignInManager.SignInAsync时抛出异常。

报错信息

InvalidOperationException: Cannot use table 'AspNetRoleClaims' for entity type 'IdentityRoleClaim<string>' since it is being used for entity type 'AspNetRoleClaims' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'IdentityRoleClaim<string>' on the primary key properties and pointing to the primary key on another entity type mapped to 'AspNetRoleClaims'.

登录控制器代码

public class SignInController : Controller
{
   private SignInManager<IdentityUser> _signInManager;
   private MyProjectContext _context;

   public TokenController(SignInManager<IdentityUser> signInManager, MyProjectContext context)
   {
      _signInManager = signInManager;
      _context = context;
   }

   public async Task<IActionResult> SignIn([FromQuery] string token)
   {
      string emailFromToken = MyTokenParser.GetEmail(token);

      IdentityUser user = new()
      {
         UserName = emailFromToken,
         Email = emailFromToken,
         EmailConfirmed = true
      };

      await _signInManager.SignInAsync(user, isPersistent: false, null);

      return RedirectToAction("Index", "Home");
   }
}

上下文类代码(EF Core Power Tools重建)

public partial class MyProjectContext : IdentityDbContext<IdentityUser>
{
    public MyProjectContext()
    {
    }

    public MyProjectContext(DbContextOptions<MyProjectContext> options)
        : base(options)
    {
    }

    public virtual DbSet<AspNetRoleClaims> AspNetRoleClaims { get; set; }
    public virtual DbSet<AspNetRoles> AspNetRoles { get; set; }
    public virtual DbSet<AspNetUserClaims> AspNetUserClaims { get; set; }
    public virtual DbSet<AspNetUserLogins> AspNetUserLogins { get; set; }
    public virtual DbSet<AspNetUserRoles> AspNetUserRoles { get; set; }
    public virtual DbSet<AspNetUserTokens> AspNetUserTokens { get; set; }
    public virtual DbSet<AspNetUsers> AspNetUsers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<AspNetRoleClaims>(entity =>
        {
            entity.HasIndex(e => e.RoleId);

            entity.Property(e => e.RoleId).IsRequired();

            entity.HasOne(d => d.Role)
                .WithMany(p => p.AspNetRoleClaims)
                .HasForeignKey(d => d.RoleId);
        });

        modelBuilder.Entity<AspNetRoles>(entity =>
        {
            entity.HasIndex(e => e.NormalizedName, "RoleNameIndex")
                .IsUnique()
                .HasFilter("([NormalizedName] IS NOT NULL)");
        });

        modelBuilder.Entity<AspNetUserLogins>(entity =>
        {
            entity.HasKey(e => new { e.LoginProvider, e.ProviderKey });
        });

        modelBuilder.Entity<AspNetUserRoles>(entity =>
        {
            entity.HasKey(e => new { e.UserId, e.RoleId });
        });

        modelBuilder.Entity<AspNetUserTokens>(entity =>
        {
            entity.HasKey(e => new { e.UserId, e.LoginProvider, e.Name });
        });

        modelBuilder.Entity<AspNetUsers>(entity =>
        {
            entity.HasIndex(e => e.NormalizedUserName, "UserNameIndex")
                .IsUnique()
                .HasFilter("([NormalizedUserName] IS NOT NULL)");
        });
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        IConfigurationRoot configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .Build();
        optionsBuilder.UseSqlServer(configuration.GetConnectionString("MyConnectionString"));
    }
}

尝试的错误修改代码

modelBuilder.Entity<AspNetRoleClaims>(entity =>
            {
                entity.HasIndex(e => e.RoleId);

                entity.Property(e => e.RoleId).IsRequired();

                entity.HasOne(d => d.Role)
                    .WithMany(p => p.AspNetRoleClaims)
                    .HasForeignKey(d => d.RoleId);

                //Tried to declare the relationship here
                entity.HasOne(d => d.RoleId)
                    .WithOne(p => p.*) //compiler error regardless of entry
                    .HasForeignKey(d => d.*) //compiler error regardless of entry
            });

请问我遗漏了什么?该如何解决这个问题?


解决方案

这个问题的核心是:你的MyProjectContext继承了IdentityDbContext<IdentityUser>,而IdentityDbContext已经内置了对IdentityRoleClaim<string>、IdentityRole等Identity默认实体的映射,对应数据库里的AspNetRoleClaims、AspNetRoles等表。但你又通过EF Core Power Tools生成了自己的AspNetRoleClaims、AspNetRoles等实体类,导致EF Core认为同一个表被两个不同的实体类型映射,从而引发冲突。

解决步骤如下:

  1. 删除手动生成的Identity相关实体类
    直接删除AspNetRoleClaims、AspNetRoles、AspNetUserClaims、AspNetUserLogins、AspNetUserRoles、AspNetUserTokens、AspNetUsers这些类,因为IdentityDbContext已经提供了对应的默认实体(IdentityRoleClaim<TKey>、IdentityRole<TKey>、IdentityUserClaim<TKey>等)。

  2. 修改上下文类,移除自定义的Identity相关配置
    把MyProjectContext里的自定义Identity相关DbSet和OnModelCreating里的对应配置删除,只保留业务实体相关代码。修改后的上下文示例:

    public partial class MyProjectContext : IdentityDbContext<IdentityUser>
    {
        public MyProjectContext()
        {
        }
    
        public MyProjectContext(DbContextOptions<MyProjectContext> options)
            : base(options)
        {
        }
    
        // 仅保留你的业务实体DbSet,例如:
        // public virtual DbSet<Order> Orders { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            // 仅添加业务实体的映射配置,删除所有AspNet开头的实体配置
    
            partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
        }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
            IConfigurationRoot configuration = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
            optionsBuilder.UseSqlServer(configuration.GetConnectionString("MyConnectionString"));
        }
    }
    
  3. 如需自定义Identity实体,采用继承方式
    如果需要扩展Identity实体(比如给用户添加额外字段),不要手动生成实体类,而是继承默认Identity实体实现:

    public class ApplicationUser : IdentityUser
    {
        // 自定义字段
        public string FullName { get; set; }
    }
    

    然后修改上下文继承IdentityDbContext<ApplicationUser>,并在Program.cs中配置Identity使用自定义实体:

    builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<MyProjectContext>();
    
  4. 修正登录逻辑(可选)
    你的代码中直接创建新的IdentityUser对象调用SignInAsync,这种"无密码登录"逻辑通常应该基于数据库中已存在的用户。建议先从数据库查询用户再执行登录:

    var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == emailFromToken);
    if (user != null)
    {
        await _signInManager.SignInAsync(user, isPersistent: false);
    }
    // 处理用户不存在的异常情况
    

内容的提问来源于stack exchange,提问作者N.MORR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:48:23