Application data
.NET Core ASP.NET Core Identity C# Entity Framework Core Visual Studio

How to Seed Data Within Your .NET Core Applications

Welcome to today’s post.

There are a few prerequisites you will need before understanding the content of this post:

  1. Entity Framework Core.
  2. ASP.NET Core Identity Framework.

I will show how you can seed your .NET Core applications.

What is seeding?

Seeding is just pre-populating your application with data that allows your application to minimally function when initially started.

There are three categories of data that can be seeded in the application:

  1. User account data.
  2. Sample records.
  3. General lookup data.

I will show examples of each of the above in this post. Firstly, for user account data.

In the Startup.cs source file, ensure that the data context id configured to your SQL data, and identity roles are configured in ConfigureServices() as shown:

services.AddDbContext<ApplicationDbContext>(options =>                
   options.UseSqlServer(Configuration.GetConnectionString(connStr)));

services.AddIdentity<ApplicationUser, IdentityRole>()
   .AddEntityFrameworkStores<ApplicationDbContext>()
   .AddDefaultTokenProviders();

More details of configuring your application for identity management can be found in a previous post.

Next, add a method. Call it Initialize().

public void Initialize(ApplicationDbContext context)
{
   .. 
}

Leave this empty for now.

With seeding user account data, we proceed as follows:

Create another class. Call it SeedAccounts.

public class SeedAccounts
{
   private ApplicationDbContext db;
   private AppConfiguration appConfiguration;

   public SeedAccounts(ApplicationDbContext _db, AppConfiguration _appConfiguration)
   {
      db = _db;
      appConfiguration = _appConfiguration;
   }

   ..
}

Note: We can use a custom class to read in the application settings. I demonstrated how this was done in a previous blog.

Next, add a method. Call it GenerateUserAccounts():

public async Task GenerateUserAccounts()
{
}

Inside this method, we can access the user store and role manager with:

var userManager = new UserStore<ApplicationUser>(db);
var roleManager = new RoleStore<IdentityRole>(db);

Creating a new role can be done as follows:

// Create Admin Role and Admin User    
if (!roleManager.Roles.Where(a => a.Name == "Admin").Any())
{
   // first we create Admin role 
   var role = new Microsoft.AspNetCore.Identity.IdentityRole();
   role.Name = "Admin";
   role.NormalizedName = role.Name.ToUpper();
   await roleManager.CreateAsync(role);
}

Creating a new user account can be done as follows:

// create Admin user
if (!userManager.Users.Where(a => a.UserName ==   
   appConfiguration.AdminEmail).Any())
{
   try
   {
      // Create a Admin super user who will maintain the website
      var user = new ApplicationUser();
      user.UserName = appConfiguration.AdminEmail;
      user.Email = appConfiguration.AdminEmail;
      user.NormalizedEmail = 
         appConfiguration.AdminEmail.ToUpper();
      user.NormalizedUserName = 
         appConfiguration.AdminEmail.ToUpper();
      user.SecurityStamp = Guid.NewGuid().ToString("D");
      var password = new PasswordHasher<ApplicationUser>();
      var hashed = password.HashPassword(user, 
         appConfiguration.AdminPwd);
      user.PasswordHash = hashed;
      var chkUser = await userManager.CreateAsync(user);

      //Add default User to Role Admin   
      if (chkUser.Succeeded)
      {
         var adminRole = roleManager.FindByNameAsync("Admin");
         var adminUser = userManager.FindByEmailAsync(appConfiguration.AdminEmail);
         if (adminRole != null && adminUser != null)
         {
            db.UserRoles.Add(new IdentityUserRole<string>()
            {
               RoleId = adminRole.Result.Id.ToString(),
               UserId = adminUser.Result.Id.ToString()
            });
            await db.SaveChangesAsync();
         }
      }
   }
   catch (Exception ex)
   {
      Console.Write("Error " + ex.Message.ToString());
   }
}

This method uses the CreateAsync() and FindByEmailAsync() async methods of the UserManager class and FindByNameAsync() async methods of the RoleManager class. We also make use of the HashPassword() method of the PasswordHasher class.

To persist changes we use the async SaveChangesAsync() method.

Seeding sample data is as follows:

Create another class. Call it SeedData.

public class SeedData
{
   private ApplicationDbContext db;

   public SeedData(ApplicationDbContext _db)
   {
      db = _db;
   }
   ..
   ..
}

Include a constructor parameter for the application data context.

Create a method that will be used to generate some sample data.

Call it GenerateSampleData().

public void GenerateSampleData()
{
   if (db.Books.Count() > 0)
      return;

   try
   {
      BookViewModel bvm = new BookViewModel()
      {

         Title = "The Lord of the Rings",
         Author = "J. R. R. Tolkien",
         Genre = "fantasy",
         YearPublished = 1954,
         Edition = "0",
         ISBN = "",
         Location = "sydney",
         DateCreated = DateTime.Today,
         DateUpdated = DateTime.Today
      };
      db.Add(bvm);
      db.SaveChanges();
   }
   catch (Exception ex)
   {
      Console.WriteLine("Error: " + ex.Message.ToString());
   }
}

Ensure that you have SaveChanges() applied to your data context to ensure changes are persisted.

To run the seed data classes from start up, add the following calls to Initialize():

SeedData seed_data = new SeedData(context);
seed_data.GenerateSampleData();

var appOptions = new AppConfiguration();
            Configuration.GetSection("AppSettings").Bind(appOptions);

SeedAccounts seed_acc = new SeedAccounts(context, appOptions);
var task = Task.Run(async() => await
   seed_acc.GenerateUserAccounts());
var result = task.Wait(5000);

The third type of seeded data, lookup data can be loaded similarly to sample data.

Note: if there are a number of asynchronous methods used to seed your data, and the application is dependent on the seeded data, then it will make sense to include a delay before returning the result from the method and leaving the start up of the application. Without a delay, the application may try to run without dependent data and give an error on initially starting.

That’s all for this post.

I hope you found it informative and useful.

Social media & sharing icons powered by UltimatelySocial