Search code examples
c#asp.net-coreentity-framework-coreasp.net-core-mvc

ModelBuilder Entity in ASP.NET Core MVC


In ASP.NET Core 6, I need to rename the database model of ASP.NET Core Identity. I do not use APIs, but just ASP.NET Core 6 MVC.

This my code:

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

    modelBuilder.Entity<AppUser>().ToTable("Users");
    modelBuilder.Entity<AppUser>().Property(up => up.Id).HasColumnName("UserId");

    modelBuilder.Entity<IdentityRole>().ToTable("Roles");
    modelBuilder.Entity<IdentityUserRole<string>>().ToTable("UserRoles", "dbo");
    modelBuilder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims", "dbo");
    modelBuilder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins", "dbo");
}

Program file code:

builder.Services.AddDefaultIdentity<AppUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();

AppUser class:

public partial class User : IdentityUser

But I get an error at this line of code in Program file:

var app = builder.Build();

These are the error details:

System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Microsoft.AspNetCore.Identity.IdentityUser] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.UserManager1[Microsoft.AspNetCore.Identity.IdentityUser] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserManager1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.ISecurityStampValidator Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.SecurityStampValidator1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.ITwoFactorSecurityStampValidator Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.SignInManager`1[Microsoft.AspNetCore.Identity.IdentityUser]

Inner exception 1: InvalidOperationException: error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[Microsoft.AspNetCore.Identity.IdentityUser] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.

Inner exception 2: InvalidOperationException: Unable to resolve service for type 'School.Web.Data.ApplicationDbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore6[Microsoft.AspNetCore.Identity.IdentityUser,School.Web.Data.ApplicationDbContext,System.String,Microsoft.AspNetCore.Identity.IdentityUserClaim1[System.String],Microsoft.AspNetCore.Identity.IdentityUserLogin1[System.String],Microsoft.AspNetCore.Identity.IdentityUserToken1[System.String]]'.


Solution

  • Your main issue is that you didn't register the required services with the custom AppUser. You need to register UserManager, RoleManager and UserClaimsPrincipalFactory as Scoped services with the new AppUser model. Not sure if I missed something here, but to have a full control on identity models, you will have to implement custom model for each identity model and inherit from its original model. Also, you will have to implement custom UserManager and RoleManager and also UserClaimsPrincipalFactory.

    Here is how to override the default Identity (.NET 6+) (here I use int as TKey instead of string you can use string if you like) :

    Identity Models

    public sealed class ApplicationIdentityRole : IdentityRole<int> { }
    
    public sealed class ApplicationIdentityRoleClaim : IdentityRoleClaim<int> { }
    
    public sealed class ApplicationIdentityUser : IdentityUser<int> { }
    
    public sealed class ApplicationIdentityUserClaim : IdentityUserClaim<int> { }
    
    public sealed class ApplicationIdentityUserLogin : IdentityUserLogin<int> { }
    
    public sealed class ApplicationIdentityUserRole : IdentityUserRole<int> { }
    
    public sealed class ApplicationIdentityUserToken : IdentityUserToken<int> { }
    

    Identity Managers

    public sealed class ApplicationUserManager : UserManager<ApplicationIdentityUser>
    {
        public ApplicationUserManager(IUserStore<ApplicationIdentityUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<ApplicationIdentityUser> passwordHasher, IEnumerable<IUserValidator<ApplicationIdentityUser>> userValidators, IEnumerable<IPasswordValidator<ApplicationIdentityUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<ApplicationIdentityUser>> logger) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
        {
        }
    }
    
    public sealed class ApplicationRoleManager : RoleManager<ApplicationIdentityRole>
    {
        public ApplicationRoleManager(IRoleStore<ApplicationIdentityRole> store, IEnumerable<IRoleValidator<ApplicationIdentityRole>> roleValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, ILogger<RoleManager<ApplicationIdentityRole>> logger) : base(store, roleValidators, keyNormalizer, errors, logger)
        {
        }
    }
    
    
    public sealed class ApplicationUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationIdentityUser, ApplicationIdentityRole>
    {
        public ApplicationUserClaimsPrincipalFactory(UserManager<ApplicationIdentityUser> userManager, RoleManager<ApplicationIdentityRole> roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
        {
        }
    }
    

    IdentityDbContext

    public sealed class ApplicationDbContext : IdentityDbContext<ApplicationIdentityUser, ApplicationIdentityRole, int, ApplicationIdentityUserClaim, ApplicationIdentityUserRole, ApplicationIdentityUserLogin, ApplicationIdentityRoleClaim, ApplicationIdentityUserToken>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<ApplicationIdentityUser>().ToTable("IdentityUsers");
            modelBuilder.Entity<ApplicationIdentityRole>().ToTable("IdentityRoles");
            modelBuilder.Entity<ApplicationIdentityRoleClaim>().ToTable("IdentityRoleClaims");
            modelBuilder.Entity<ApplicationIdentityUserRole>().ToTable("IdentityUserRoles");
            modelBuilder.Entity<ApplicationIdentityUserClaim>().ToTable("IdentityUserClaims");
            modelBuilder.Entity<ApplicationIdentityUserLogin>().ToTable("IdentityUserLogins");
            modelBuilder.Entity<ApplicationIdentityUserToken>().ToTable("IdentityUserTokens");
    
        }
    }
    

    DI Services

    builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
       
    builder.Services.AddIdentity<ApplicationIdentityUser, ApplicationIdentityRole>(options => 
    { 
        // set Identity options here
    })
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddUserManager<ApplicationUserManager>()
        .AddRoleManager<ApplicationRoleManager>()                
        .AddClaimsPrincipalFactory<ApplicationUserClaimsPrincipalFactory>()  
        .AddDefaultTokenProviders();
    

    You can explore the rest of the options that already provided via the service collection (e.g. AddSignInManager), to have an idea on the configurable options.