Search code examples
c#entity-framework-coredbcontextasp.net-core-identityusermanager

How to inject userManager inside the DbContext constructor?


I'm using IdentityDbContext and I have a SeedData class that I'm using inside the OnModelCreating method to seed the database using the initial migrations as follows:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    //
    //
    SeedData seedData = new(_userManager);
    seedData.SeedWebsiteAdmins(modelBuilder.Entity<WebsiteAdmin>());
    seedData.SeedCompanyAdmins(modelBuilder.Entity<CompanyAdmin>());
    //
    //
}

This SeedData class requires a parameter of type UserManager inside its constructor because it needs to create the new users as follows:

public async void SeedWebsiteAdmins(EntityTypeBuilder<WebsiteAdmin> entity)
{
    var superAdminLogin = new ApplicationUser()
    {
        Name = "Name",
        Email = "Email",
        UserType = UserType.WebsiteAdmin
    };

    var result = await _userManager.CreateAsync(superAdminLogin, "{password}");

    if (result.Succeeded)
    {
        await _userManager.AddToRoleAsync(superAdminLogin, RoleNames.WebsiteAdminRole);
    }

    var superAdmin = new WebsiteAdmin()
    {
        Id = SuperAdminId,
        WebsiteAdminLoginId = superAdminLogin.Id,
        MaxServiceDiscount = 100,

    };

    entity.HasData(superAdmin);
}

I've tried injecting the UserManager inside the DbContext constructor, but it's causing the following error:

System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore`3[RepositoryProject.DomainAuth.ApplicationUser,Microsoft.EntityFrameworkCore.DbContext,System.Guid]'

How can I inject the UserManager in the DbContext constructor without causing any error?

Or is there a way to create the identity user without using the UserManager so I can delete the dependency from the SeedData class and hence I wouldn't need it inside the DbContext?


Solution

  • Try this:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        var userManager = this.GetService<UserManager>();
        ...
    

    One thing though, I would do this on your IdentityDbContext implementation using HasData as suggested in one of the comments above NOT in the DbContext OnModelCreating ie.

    public MyIdentityDbContext : IdentityDbContext<ApplicationUser, IdentityRole, string> {
        
        public MyIdentityDbContext(DbContextOptions<MyIdentityDbContext> options)
            : base(options)
        {
        }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
      
            SetupData(builder);
        }
    
        private void SetupData(ModelBuilder builder)
        {
            // Users
            var defaultUsers = new List<ApplicationUser>() { 
                new ApplicationUser()
                {
                    Id = "USER_HASH"
                    Name = "name",
                    Email = "[email protected]",
                    NormalizedEmail = "[email protected]",
                    UserName = "username",
                    NormalizedUserName  = "NormalizedUserName",
                    UserType = UserType.WebsiteAdmin,
                    // Use your known hash here (the hash below is for Password123!)
                    PasswordHash = "PasswordHash",
                    // Use your hash here
                    SecurityStamp = "SecurityStamp",
                    AccessFailedCount = 0,
                    EmailConfirmed = true,
                    PhoneNumberConfirmed = true,
                    TwoFactorEnabled = false
                }
            };
    
            builder.Entity<ApplicationUser>().HasData(defaultUsers);
    
            // Roles
            var defaultRoles = new List<IdentityRole>(){ 
                new IdentityRole { Id = "ROLE_HASH", Name = 
                RoleNames.WebsiteAdminRole, NormalizedName = 
                RoleNames.WebsiteAdminRole.ToUpper() }
            };
    
            builder.Entity<IdentityRole>().HasData(defaultRoles);
    
            // User roles
            var defaultUserRoles = new List<IdentityUserRole<string>>(){ 
                new IdentityUserRole<string>(){
                   Id = "USER_HASH",
                   RoleId = "ROLE_HASH" 
                }
            };
    
            builder.Entity<IdentityUserRole<string>>().HasData(defaultUsersRoles);
        }
    
    }
    

    Note: The hash I have for the password above is with API set to use IdentityV3 ie.

    builder.Services.Configure<PasswordHasherOptions>(options => options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV3);
    

    You can create your own password hash using

    var passwordHasher = this.GetService<IPasswordHasher<IdentityUser>>();
    var passwordHash = passwordHasher.HashPassword(user, "YourPassword");