Complex Data Seeding in .NET Core with EF Core and Bogus
Introduction
This tutorial demonstrates how to create a sophisticated seeding system in a .NET Core Web API project. Using Bogus and Entity Framework Core, we’ll seed complex relational data, conditional on the environment type, allowing seamless data setup for development and testing.
Prerequisites
- .NET Core SDK
- Visual Studio or any code editor
- Basic understanding of C#, Entity Framework Core, and relational databases
Steps
Step 1: Create a New .NET Core Project
Start by creating a new .NET Core Web API project.
dotnet new webapi -n ComplexSeedingApp
cd ComplexSeedingApp
Step 2: Install Required Packages
Add the necessary NuGet packages for Entity Framework Core and Bogus.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Bogus
Step 3: Define Complex Data Models
We’ll create models representing Users, Profiles, Roles, and Permissions, with relationships to demonstrate more complex seeding.
// Models/User.cs
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public Profile Profile { get; set; }
public ICollection<Role> Roles { get; set; }
}
// Models/Profile.cs
public class Profile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
// Models/Role.cs
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Permission> Permissions { get; set; }
}
// Models/Permission.cs
public class Permission
{
public int Id { get; set; }
public string Name { get; set; }
}
Step 4: Configure DbContext with Conditional Seeding Logic
Add DbSets to DbContext
and implement a seeding method that generates data using Bogus. This logic will only seed data for the Development and Test environments.
// MyDbContext.cs
using Bogus;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Data;
using System.Reflection.Emit;
using System.Security;
public class MyDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Permission> Permissions { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" ||
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Test")
{
SeedData(modelBuilder);
}
}
private void SeedData(ModelBuilder modelBuilder)
{
// Seed Permissions
var permissionFaker = new Faker<Permission>()
.RuleFor(p => p.Name, f => f.Company.CatchPhrase());
var permissions = permissionFaker.Generate(10);
modelBuilder.Entity<Permission>().HasData(permissions);
// Seed Roles with Permissions
var roleFaker = new Faker<Role>()
.RuleFor(r => r.Name, f => f.Name.JobTitle())
.RuleFor(r => r.Permissions, f => f.PickRandom(permissions, 3).ToList());
var roles = roleFaker.Generate(5);
modelBuilder.Entity<Role>().HasData(roles);
// Seed Users with Profiles and Roles
var userFaker = new Faker<User>()
.RuleFor(u => u.Username, f => f.Internet.UserName())
.RuleFor(u => u.Email, f => f.Internet.Email())
.RuleFor(u => u.Roles, f => f.PickRandom(roles, 2).ToList());
var users = userFaker.Generate(20);
var profileFaker = new Faker<Profile>()
.RuleFor(p => p.FirstName, f => f.Name.FirstName())
.RuleFor(p => p.LastName, f => f.Name.LastName())
.RuleFor(p => p.UserId, f => f.PickRandom(users).Id);
var profiles = profileFaker.Generate(20);
modelBuilder.Entity<User>().HasData(users);
modelBuilder.Entity<Profile>().HasData(profiles);
}
}
Step 5: Set Up Application Seeding on Start
To ensure the database seeds only in Development and Test environments, configure seeding within Startup.cs
. We’ll check the environment and invoke seeding accordingly.
// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer("YourConnectionStringHere"));
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
{
if (env.IsDevelopment() || Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Test")
{
// Ensures database is created and seeded only in Development or Test environments
context.Database.EnsureCreated();
}
}
}
Step 6: Ensure Database Re-seeding
When working in development and test environments, it’s often helpful to reset and reseed the database each time you restart the application. You can accomplish this by dropping the database if it exists and re-creating it during the Configure
phase.
// Startup.cs (Updated)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
{
if (env.IsDevelopment() || Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Test")
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}
}
Conclusion
This tutorial provided a more advanced seeding setup without using a CLI tool. By configuring seeding to run automatically based on the environment, you create realistic test data for development and testing while protecting production. This setup allows for a flexible and robust approach to database seeding that supports complex relationships, improving your application’s test and development experience.