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

ASP.NET Core Identity与EF Core多用户类型实现报错求助

ASP.NET Identity 多用户类型(Customer/Professional)配置问题

需求背景

我的应用中有Customer和Professional两类用户,使用ASP.NET Identity管理用户。此前仅通过在ApplicationUser中添加标记区分两类用户,操作繁琐。现在希望将通用身份信息存储在Identity框架默认表中,Customer专属信息存于Customers表,Professional专属信息存于Professionals表。

已定义的领域实体

public class Account : IdentityUser<Guid>
{
    public string FullName { get; set; } = string.Empty;
    public string ImageUrl { get; set; } = string.Empty;
    public bool HasAcceptedTermsOfUse { get; set; } = default;
}

public class Customer : Account
{
    // Customer专属属性
}

public class Professional : Account
{
    // Professional专属属性
}

修改后的DbContext配置

public class XenDbContext: IdentityDbContext<Account, IdentityRole<Guid>, Guid>
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Professional> Professionals { get; set; }
    // 其他DbSet

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

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.Entity<Account>(entity => { entity.ToTable("Accounts"); });
        builder.Entity<Customer>(entity => { entity.ToTable("Customers"); });
        builder.Entity<Professional>(entity => { entity.ToTable("Professionals"); });
        builder.Entity<IdentityRole<Guid>>(entity => { entity.ToTable("Roles"); });
        builder.Entity<IdentityUserRole<Guid>>(entity => { entity.ToTable("AccountRoles"); });
        builder.Entity<IdentityUserClaim<Guid>>(entity => { entity.ToTable("AccountClaims"); });
        builder.Entity<IdentityUserLogin<Guid>>(entity => { entity.ToTable("AccountLogins"); });
        builder.Entity<IdentityUserToken<Guid>>(entity => { entity.ToTable("AccountTokens"); });
        builder.Entity<IdentityRoleClaim<Guid>>(entity => { entity.ToTable("RoleClaims"); });
    }
}

运行时错误信息

System.InvalidOperationException
HResult=0x80131509
消息: Scheme already exists: Identity.Application
来源: Microsoft.AspNetCore.Authentication.Abstractions

调用堆栈:
at Microsoft.AspNetCore.Authentication.AuthenticationOptions.AddScheme(String name, Action1 configureBuilder) at Microsoft.AspNetCore.Authentication.AuthenticationBuilder.<>c__DisplayClass4_02.b__0(AuthenticationOptions o)
at Microsoft.Extensions.Options.ConfigureNamedOptions1.Configure(String name, TOptions options) at Microsoft.Extensions.Options.OptionsFactory1.Create(String name)
at Microsoft.Extensions.Options.UnnamedOptionsManager1.get_Value() at Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider..ctor(IOptions1 options, IDictionary2 schemes) at Microsoft.AspNetCore.Authentication.AuthenticationSchemeProvider..ctor(IOptions1 options)
at System.RuntimeMethodHandle.InvokeMethod(Object target, Span1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType) at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_0.b__0(RequestDelegate next)
at Microsoft.AspNetCore.Builder.ApplicationBuilder.Build()
at Microsoft.AspNetCore.Builder.WebApplicationBuilder.b__27_0(RequestDelegate next)
at Microsoft.AspNetCore.Builder.ApplicationBuilder.Build()
at Microsoft.AspNetCore.Hosting.GenericWebHostService.d__37.MoveNext()
at Microsoft.Extensions.Hosting.Internal.Host.d__12.MoveNext()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.d__4.MoveNext()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.d__4.MoveNext()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Microsoft.AspNetCore.Builder.WebApplication.Run(String url)
at Xen.Presentation.API.Program.Main(String[] args) in C:\Users\Arthur\Development\Xen\src\backend\Presentation\Xen.Presentation.API\Program.cs:line 53

持久化层服务注册代码

public static class PersistenceServiceRegistration
{
    public static void AddPersistenceServices(this IServiceCollection services, IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("XenDbLocalConnectionString");
        
        services.AddDbContext<XenDbContext>(options => options.UseSqlServer(connectionString));

        services.AddIdentity<Account, IdentityRole<Guid>>(options =>
        {
            options.Password.RequireDigit = false;
        }).AddEntityFrameworkStores<XenDbContext>().AddDefaultTokenProviders();

        services.AddIdentity<Customer, IdentityRole<Guid>>(options =>
        {
            options.Password.RequireDigit = false;
        }).AddEntityFrameworkStores<XenDbContext>().AddDefaultTokenProviders();

        services.AddIdentity<Professional, IdentityRole<Guid>>(options =>
        {
            options.Password.RequireDigit = false;
        }).AddEntityFrameworkStores<XenDbContext>().AddDefaultTokenProviders();

        // 注册仓储
    }
}

核心疑问

我希望通过Account为两类用户生成令牌,但目前因多次调用AddIdentity导致认证Scheme冲突。注释掉app.UseAuthorization()可以运行,但我需要完整的身份验证和授权功能。请问我的需求是否可行?若可行,该如何实现?


解决方案

你的需求完全可行,错误根源是多次调用AddIdentity会重复注册同名的认证Scheme(Identity.Application),ASP.NET Identity默认只支持一个主用户类型的核心注册,多用户类型需基于基类扩展实现。

1. 修正服务注册代码

移除重复的AddIdentity调用,仅保留基于基类Account的一次注册:

public static class PersistenceServiceRegistration
{
    public static void AddPersistenceServices(this IServiceCollection services, IConfiguration configuration)
    {
        var connectionString = configuration.GetConnectionString("XenDbLocalConnectionString");
        
        services.AddDbContext<XenDbContext>(options => options.UseSqlServer(connectionString));

        // 仅注册一次基于Account的Identity核心服务
        services.AddIdentity<Account, IdentityRole<Guid>>(options =>
        {
            options.Password.RequireDigit = false;
        }).AddEntityFrameworkStores<XenDbContext>().AddDefaultTokenProviders();

        // 注册自定义业务服务(如用户类型查询、专属逻辑处理)
        // services.AddScoped<ICustomerService, CustomerService>();
        // services.AddScoped<IProfessionalService, ProfessionalService>();

        // 注册仓储
    }
}

2. 理解继承映射逻辑

你当前的DbContext配置使用了**TPT(表每类型)**继承映射,Account作为基类对应Accounts表,Customer和Professional作为子类分别对应Customers和Professionals表,EF Core会自动维护表间的关联关系,无需额外配置。

3. 多用户类型的业务处理

  • 用户注册:创建对应类型的实例(如Customer或Professional),直接调用UserManager<Account>.CreateAsync()即可,EF Core会自动将数据写入基表和对应子表。
  • 用户查询:通过DbContext的Customers/Professionals集合查询专属信息,或先获取Account实例后通过类型转换判断用户类型(如account is Customer customer)。
  • 授权控制:
    • 登录时通过ClaimsTransformer添加用户类型的Claim(如new Claim("UserType", "Customer")),后续可通过授权策略验证该Claim。
    • 在业务逻辑中直接判断用户类型,实现专属权限控制。

4. 令牌生成

由于Customer和Professional都是Account的子类,基于Account注册的Identity服务生成的令牌会自动覆盖两类用户,无需额外配置。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 19:10:00