Seeding an Asp.Net Core 2 Code First Database with Data and User Accounts

Entity Framework Core

I had a requirement to seed an enterprise application with some data in an Asp.Net Core 2.0 application. There is no native support and the most popular solutions elsewhere on the web were either overly-complicated, or involved manipulating the Main method. 

In the end I wrote a solution to call a seeding method from the Configure class in Startup.cs, like this: 

app.SeedDatabase();

The Configure class is generally used to set up your HTTP middleware, however I personally preferred this solution as it looks neater and ties in with the rest of the startup functionality in my web application. 

Seeding Data 

First of all you need to extend the IApplicationBuilder class. I placed this in a class inside a ‘Middleware’ folder with my other extension methods of this class. The convention is to include this in the Microsoft.AspNetCore.Builder namespace, but I’ll leave that decision to you.

public static class ApplicationBuilderExtensions 
{
    public static IApplicationBuilder SeedDatabase(this IApplicationBuilder app) 
    { 
        IServiceProvider serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider; 
        try 
        { 
            var context = serviceProvider.GetService<MyContext>(); 
            DatabaseSeeder.InsertSeedData(context); 
         } 
        catch (Exception ex) 
        {
            var logger = serviceProvider.GetRequiredService<ILogger<Program>>(); 
            logger.LogError(ex, "An error occurred while seeding the database."); 
        } 
        return app; 
    } 
}

Next, you need to create a class and method to call your seeding logic. Here I am checking if a table is empty, if it’s empty then I populate some default data. This is great when deploying your app to a new environment.  

public static void InsertSeedData(MyContext context) 
{ 
    if (!context.UserTypes.Any()) 
    { 
        context.UserTypes.AddRange( 
                new UserType { Name = "Employee" }, 
                new UserType { Name = "Client" }, 
                new UserType { Name = "Supplier" } 
                ); 

        context.SaveChanges(); 
    } 
}

Finally, we just need to call the extended method from Configure in Startup.cs: 

public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
{ 
    // … Your Middleware config goes here 

    app.UseMvcWithDefaultRoute(); 

    app.SeedDatabase(); 
}

Seeding User Accounts 

Of course, many developers like to seed a default admin account, as once your authorisation is in place it can cause frustrations if you don’t have any user accounts to login with. 

One of the main concerns is how to avoid hard-coding the admin password into the method thus checking it in to source control. 

.Net Core has a handy way of dealing with this: any sensitive data can be kept in an appsettings.development.json file, which you can exclude from source control. 

First of all, let’s set up this file. If you don’t have an appsettings.development.json file (or equivalent), you can simply copy your existing appsettings.json and re-name it. 

Once that’s in place you need to add your desired admin account details to the file. 

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=jdono;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "AdminAccount": {
    "username": "sysadmin",
    "password": "jdono.com1",
    "role": "sysadmin",
    "email":  "james@jdono.co.uk"
  }
}

We will need to ask the resolver for aIConfiguration to use the built in appsettings configuration features and a UserManager/RoleManager to deal with the User/Roles stuff.  

// ..
var configuration = serviceProvider.GetService<IConfiguration>(); 
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>(); 
var roleManager = serviceProvider.GetService<RoleManager<ApplicationRole>>(); 
DatabaseSeeder.InsertDefaultAdminAccount(configuration, userManager, roleManager); 
// ..

Here’s how it now looks in our SeedDatabase method: 

public static IApplicationBuilder SeedDatabase(this IApplicationBuilder app) 
{ 
    IServiceProvider serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider; 
    try 
    { 
        var context = serviceProvider.GetService<MyContext>(); 
        DatabaseSeeder.InsertSeedData(context); 

        var configuration = serviceProvider.GetService<IConfiguration>(); 
        var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>(); 
        var roleManager = serviceProvider.GetService<RoleManager<ApplicationRole>>(); 
        DatabaseSeeder.InsertDefaultAdminAccount(configuration, userManager, roleManager); 
    } 
    catch (Exception ex) 
    { 
        var logger = serviceProvider.GetRequiredService<ILogger<Program>>(); 
        logger.LogError(ex, "An error occurred while seeding the database."); 
    } 

    return app; 
}

As far as I can see from the documentation (and please correct me if I’m wrong) the IServiceProvider deals with disposing the services it called using dependency injection so we don’t need to manually call dispose, or use a using statement. 

Finally, let’s create the InsertDefaultAdminAccount method to deal with inserting the role and the user if they don’t exist, and adding the user to the role. 

public static async void InsertDefaultAdminAccount( 
          IConfiguration configuration, 
          UserManager<ApplicationUser> userManager, 
          RoleManager<ApplicationRole> roleManager) 
{ 
    string username = configuration["AdminAccount:username"]; 
    string email = configuration["AdminAccount:email"]; 
    string password = configuration["AdminAccount:password"]; 
    string role = configuration["AdminAccount:role"]; 

    if (!(await roleManager.RoleExistsAsync(role))) 
    { 
        await roleManager.CreateAsync(new ApplicationRole { Name = role }); 
    } 

    //create the default admin account 
    if (!userManager.Users.Any(u => u.Name == username)) 
    { 
        var user = new ApplicationUser() { UserName = username, Email = email }; 
        var result = await userManager.CreateAsync(user, password); 

        if (result.Succeeded) 
        { 
            await userManager.AddToRoleAsync(user, role); 
        } 
    } 
}

Credit goes to Emmanuel for some of the code used in the SeedDatabase method. 

Hello world!

Thanks for visiting my blog. I’ll be writing about .Net, C# and other Microsoft stack related stuff.

I’m a mid-level developer working primarily alone at a small organisation. I’m currently working on a greenfield full scale enterprise case management system and I’ll be blogging about my endeavours with this project.

You may find it humorous that I’m blogging about .Net related matters using WordPress, but there are a couple of reasons for this..

Firstly, all of my experience has previously been with Microsoft tech, so I thought I’d improve my understanding of how “30% of the Web” works.

WordPress: 30% of the Web

The second reason, if you’re a developer like me, is something I’m sure you’ll be familiar with: time. When I have some personal time free to build my own .Net blogging site I’ll be doing just that!