How do I seed user roles in Identity ASP.NET Core 2.1 (Identity Scaffolded)
Asked Answered
W

3

3

How do I seed a "admin" user role when identity is scaffolded?

I would like to find my power-user account by email and seed the admin role. Most examples I find use Startup.cs, but you are supposed to use IdentityHostingStartup.cs to register identity related services.

So where/how do I inject the RoleManager and UserManager in IdentityHostingStartup? (I assume this is what I need, please let me know if there is a better way)

  public class IdentityHostingStartup : IHostingStartup
  {
        public void Configure(IWebHostBuilder builder)
        {
            builder.ConfigureServices((context, services) => {
                services.AddDbContext<MyWebContext>(options =>
                    options.UseSqlServer(
                        context.Configuration.GetConnectionString("MyWebContextConnection")));

                services.AddIdentity<MyWebUser, MyWebRole>()
                    .AddRoles<MyWebRole>()
                    .AddRoleManager<RoleManager<MyWebRole>>()
                    .AddDefaultUI()
                    .AddEntityFrameworkStores<MyWebContext>();

                services.Configure<IdentityOptions>(options => {
                    options.Password.RequireNonAlphanumeric = false;
                    });
            });
        }
  }
Warder answered 8/10, 2018 at 18:52 Comment(0)
I
8

You cannot perform data seeding on the web host builder since at that time, the service provider has not been built yet. Instead, you will have to actually create the web host first before you can resolve any services you have registered before.

My usual recommendation is to perform the initial seeding in the Program.cs after the WebHost is created. Following the default template, I would adjust the Main method to look like this:

public static async Task Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var roleManager = scope.ServiceProvider.GetService<RoleManager<MyWebRole>>();

        if (!await roleManager.RoleExistsAsync("admin"))
            await roleManager.CreateAsync(new MyWebRole { Name = "admin" });
    }

    await host.RunAsync();
}

So this will first create the web host, then it will create a dependency injection scope from which it will resolve a RoleManager instance. Using that manager you then can create the roles you require.

You could also create a separate service for this, so you do not need to have all that logic inside the Program.cs but can just rely on some other service to perform the data seeding:

public static async Task Main(string[] args)
{
    var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var dataSeeder = scope.ServiceProvider.GetService<DataSeeder>();
        await dataSeeder.EnsureSeedDataAsync();
    }

    await host.RunAsync();
}

The DataSeeder would then be registered with the dependency injection container. And then it could take the RoleManager and other services (e.g. options, or even the database context) as a dependency and perform the seeding in the EnsureSeedDataAsync method.

An alternative would be to use the Entity Framework Core data seeding functionality using the .HasData() method call on the model builder. This however requires fixed IDs on your role objects and you will also have to create the objects at database level instead of relying on the higher-level RoleManager.

Impetus answered 8/10, 2018 at 20:48 Comment(3)
It's a bit confusing as to what is happening with Migrations when using .HasData(). First, I added roles for asp.net identity there without explicitly providing an Id. That led to the roles being regenerated for each migration. Then I provided Id's and that still leads to the migrationbuilder updating roles on each and every subsequent migration.Gonorrhea
I have an exception if the database isnt created yet. How can i resolve that?. If i comment the line 2await dataSeeder.EnsureSeedDataAsync();", start the application, stop the application, uncomment that line and start again, it work perfectlyZeena
@Zeena You can use dbContext.Database.EnsureCreatedAsync() or dbContext.Database.MigrateAsync() (when using migrations) to ensure that your database exists (or is migrated to the latest version).Impetus
M
2

a bit old thread but this may help anyone seeking it. You can seed Users and Roles in OnModelCreating() method inside IdentityDbContext.cs file as shown below. Note that the keys should be predefined to avoid seeding new users and new roles everytime this method is executed:

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

        //Seeding a  'Administrator' role to AspNetRoles table
        modelBuilder.Entity<IdentityRole>().HasData(new IdentityRole {Id = "2c5e174e-3b0e-446f-86af-483d56fd7210", Name = "Administrator", NormalizedName = "ADMINISTRATOR".ToUpper() });


        //a hasher to hash the password before seeding the user to the db
        var hasher = new PasswordHasher<IdentityUser>();


        //Seeding the User to AspNetUsers table
        modelBuilder.Entity<IdentityUser>().HasData(
            new IdentityUser
            {
                Id = "8e445865-a24d-4543-a6c6-9443d048cdb9", // primary key
                UserName = "myuser",
                NormalizedUserName = "MYUSER",
                PasswordHash = hasher.HashPassword(null, "Pa$$w0rd")
            }
        );


        //Seeding the relation between our user and role to AspNetUserRoles table
        modelBuilder.Entity<IdentityUserRole<string>>().HasData(
            new IdentityUserRole<string>
            {
                RoleId = "2c5e174e-3b0e-446f-86af-483d56fd7210", 
                UserId = "8e445865-a24d-4543-a6c6-9443d048cdb9"
            }
        );
        

    }
Massengale answered 13/8, 2020 at 8:3 Comment(0)
O
0

My Local version EF Core 6.0.3 required a Primary Key for uniqueness.

Updated Code:

//Seeding the relation between our user and role to AspNetUserRoles table

modelBuilder.Entity<IdentityUserRole<Guid>>().HasKey(x => new { x.RoleId, x.UserId });

modelBuilder.Entity<IdentityUserRole<string>>().HasData(
new IdentityUserRole<string>
            {
                RoleId = "2c5e174e-3b0e-446f-86af-483d56fd7210", 
                UserId = "8e445865-a24d-4543-a6c6-9443d048cdb9"
            }
        );
Outhaul answered 19/6, 2022 at 3:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.