When we create our app it’s good to have some data in a database at some point, for example test user accounts or other values if we want to test something. How to populate database at the start of application if the database is empty?
First we need to create new class in our project. Let’s see how to do it on example.
I want to seed the database with test user with admin role, because I want to test loging functionality.
Here’s my class with Seed method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public class ELPIdentityInitializer { private RoleManager<IdentityRole> _roleMgr; private UserManager<User> _userMgr; public ELPIdentityInitializer(UserManager<User> userMgr, RoleManager<IdentityRole> roleMgr) { _userMgr = userMgr; _roleMgr = roleMgr; } public async Task Seed() { var user = await _userMgr.FindByNameAsync("TestAdminUser"); if (user == null) { if (!(await _roleMgr.RoleExistsAsync("Admin"))) { var role = new IdentityRole("Admin"); role.Claims.Add(new IdentityRoleClaim<string>() { ClaimType = "IsAdmin", ClaimValue = "True" }); await _roleMgr.CreateAsync(role); } user = new User() { UserName = "TestAdminUser", FirstName = "Admin", LastName = "User", Email = "test@admin.com" }; var userResult = await _userMgr.CreateAsync(user, "SecretPassword!"); var roleResult = await _userMgr.AddToRoleAsync(user, "Admin"); var claimResult = await _userMgr.AddClaimAsync(user, new Claim("SuperUser", "True")); if (!userResult.Succeeded || !roleResult.Succeeded || !claimResult.Succeeded) { throw new InvalidOperationException("Failed to build user and roles"); } } } } |
What it does, it just checks if user „TestAdminUser” and role „Admin” exist in the database. If not it adds them and assigns role and claim to the user. If you are wondering what is User class, I have user class defined which is derived from IdentityUser class (Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser), so it’s IdentityUser class with additional properties.
ELPIdentityInitializer class is not complicated, but we can add here more data. The question is how to load this data and when? As you probably guess, the good place is Startup class and Configure method, because it will be called only once at the start of application. To achieve our goal we need to add another parameter to this method. In my case it will be parameter of type ELPIdentityInitializer. In the body of Configure method, at the end (after UseMvc() method call) we need to execute Seed method from our initializer. This method is awaitable, so we can just wait for completion, because we don’t want to change Configure method to be asynchronous.
1 2 3 4 5 6 7 8 9 10 11 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ELPIdentityInitializer identityInitializer) { loggerFactory.AddConsole(_configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIdentity(); app.UseMvc(); identityInitializer.Seed().Wait(); } |
Additionally we need to register our initializer class in the service collection, because without it we will see an error while resolving class. To do it let’s add this line of code to the ConfigureServices method in Startup class.
1 |
services.AddTransient<ELPIdentityInitializer>(); |
I used AddTransient method here. It means service is created each time it is requested, but it’s not important here. Our service is requested only once at the beginning. BTW Transient lifetime works best for lightweight and stateless services.
After these few steps we should see result of our work which is populated database, in this case Users table.