How to seed users, roles when I have TWO DbContext, a) IdentityDbContext & b) AppDbContext
normally I would put the code for seeding users in the OnModelCreating()
Its not an issue with one
DbContext.
Recently it was recommended to separate identity's DbContext
from the App's DbContext
, which has added two DbContexts and some confusion.
-
public UseManagementDbContext(DbContextOptions<UseManagementDbContext> options) : base(options)
// security/users separation public AppDbContext(DbContextOptions<MyDbContext> options) : base(options)
But when you have two separate DbContexts, How do I seed the user / role data?
-
IdentityDbContext
-- this has all the<IdentityUser>
context for seeding -
AppDbContext
-- this does NOT have<IdentityUser>
context, but my Migrations use this context.
Can you please help how to seed the user and role data, and is part of the migrations or startup etc.
Update: using core 6 sample, @Zhi Lv - how do I retrofit into program.cs my seedData when the app is fired up
My Program.cs
was from created originally from the ASP Core 3.1 template, it looks like this, what should I refactor, (oddly in the link at MS, there are no class name brackets, so where does my seed get setup and invoked from?
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
To seed the user and role data, you can create a static class, like this:
//required
//using Microsoft.AspNetCore.Identity;
//using Microsoft.EntityFrameworkCore;
public static class SeedRoles
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()))
{
string[] roles = new string[] { "Owner", "Administrator", "Tests2", "Editor", "Buyer", "Business", "Seller", "Subscriber" };
var newrolelist = new List<IdentityRole>();
foreach (string role in roles)
{
if (!context.Roles.Any(r => r.Name == role))
{
newrolelist.Add(new IdentityRole(role));
}
}
context.Roles.AddRange(newrolelist);
context.SaveChanges();
}
}
}
Note: In my application, I'm using Asp.net 6, the DB context is ApplicationDbContext
, you can change it to yours.
Then, in the program.cs file, you can call the seed role method after the var app = builder.Build();
line, code like this:
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>() //enable Identity Role
.AddEntityFrameworkStores<ApplicationDbContext>() ;
builder.Services.AddControllersWithViews();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedRoles.Initialize(services);
}
The result as below, after running the application, it will seed the data:
Since, you are using two DBcontext, you can also create multiple static methods to seed the data.
More detail information, you can check the official document and sample.
Update:
To seed the User, you can use the UserManager to create user in the seed method, code like this:
public static class SeedUsers
{
public static async Task InitializeAsync(IServiceProvider serviceProvider)
{
using (UserManager<IdentityUser> _userManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>())
{
var userlist = new List<SeedUserModel>()
{
new SeedUserModel(){ UserName="[email protected]",Password= "Abc12345!" },
new SeedUserModel(){UserName ="[email protected]", Password="Abc12345!"}
};
foreach (var user in userlist)
{
if (!_userManager.Users.Any(r => r.UserName == user.UserName))
{
var newuser = new IdentityUser { UserName = user.UserName, Email = user.UserName };
var result = await _userManager.CreateAsync(newuser, user.Password);
}
}
}
}
}
The screenshot is like this:
[Note] From the screenshot, we can see that by default the EmailConfirmed column is False
. If you enabled the Email Confirm operation, you need to change the value to True
, then you can login success.
Update 2:
For the previous version: such as Asp.net 5 or Asp.net core 3.1, you could add the seed initializer in the Program.cs with the following code:
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred seeding the DB.");
}
}
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Reference: Add the seed initializer.