Introduction
Learn how to seed data with Microsoft EF Core 9 using UseSeeding and UseAsyncSeeding methods, which provide a convenient way of seeding the database with initial data.
💡 AI was used to write code, for instance, creating elements to display data on the index page and the class to mock data. A developer may know how to write the code, but using AI tools like GitHub Copilot, JetBrains AI, or ChatGPT can save time better spent on business logic when a developer is well-versed with creating prompts.
Both methods assist with improving logic for populating a database with data instead of using HasData.
Overview
Migrations are not used. Instead, the database has the following table without data. The DbContext and models are reverse-engineered using EF Power Tools, a Visual Studio extension.
Code presented in an ASP.NET Core project written in Program.cs using a class for mocked data and a language extension method for the DbContext to keep the Main method clean.
Schema
UseSeeding and UseAsyncSeeding is the recommended way of seeding the database with initial data when working with EF Core.
From Microsoft
💡 On a personal note, the code provided uses only UseAsyncSeeding, which appears to function without issues. However, I have added UseSeeding as this is a Microsoft recommendation.
Step 1
Create a section in appsettings.json (or an environment variable) that determines whether to seed.
Step 2
Create data for seeding, in this example, the following class is used.
[
new() { Name = "Toyota" },
new() { Name = "Ford" },
new() { Name = "Mazda" }
];
public static List<Car> GetCars() =>
[
new() { Make = "MX5", Model = "Miata", YearOf = 2025, ManufacturerId = 3 },
new() { Make = "Mustang", Model = "GT", YearOf = 2023, ManufacturerId = 2 },
new() { Make = "Corolla", Model = "GR", YearOf = 2022, ManufacturerId = 1 }
];
}
Step 3
In Program.cs, read the setting from appsettings.json to determine if seeding is needed.
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
bool shouldSeed = builder.Configuration.GetValue<bool>(AppSettings.SeedDataEnabled);
Step 4
- Set up service for the DbContext
- Configure UseAsyncSeeding and UseSeeding
- Note that the Manufacturer data needs to be added first, as the Car data is dependent on the Manufacturer data.
In Program.cs
builder.Services.AddDbContext<Context>((serviceProvider, options) =>
{
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
var environment = serviceProvider.GetRequiredService<IWebHostEnvironment>();
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
sqlOptions.CommandTimeout(5);
}).LogTo(Console.WriteLine, LogLevel.Information);
if ((environment.IsDevelopment() || environment.IsStaging()) && shouldSeed)
{
options.UseAsyncSeeding(async (context, _, cancellationToken) =>
{
if (context.ShouldSeed<Manufacturer>())
{
await context.Set<Manufacturer>().AddRangeAsync(MockedData.GetManufacturers(), cancellationToken);
await context.SaveChangesAsync(cancellationToken);
}
if (context.ShouldSeed<Car>())
{
await context.Set<Car>().AddRangeAsync(MockedData.GetCars(), cancellationToken);
await context.SaveChangesAsync(cancellationToken);
}
});
options.UseSeeding((context, _) =>
{
if (context.ShouldSeed<Manufacturer>())
{
context.Set<Manufacturer>().AddRange(MockedData.GetManufacturers());
context.SaveChanges();
}
if (context.ShouldSeed<Car>())
{
context.Set<Car>().AddRange(MockedData.GetCars());
context.SaveChanges();
}
});
}
});
Step 5
Code to trigger seeding, dependent on value read from appsettings.json.
- db.Database.EnsureCreatedAsync() is the trigger
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// Trigger to run UseAsyncSeeding()
if (shouldSeed)
{
using var scope = app.Services.CreateScope();
// Create a scope to obtain a reference to the database context (Context)
var db = scope.ServiceProvider.GetRequiredService<Context>();
await db.Database.EnsureCreatedAsync(); // Will trigger UseAsyncSeeding
}
Step 6
Render data in the Index Page.
Backend code
public class IndexModel(Context context) : PageModel
{
[BindProperty]
public required List<Car> Cars { get; set; }
public async Task OnGetAsync()
{
Cars = await context.Car.Include(c => c.Manufacturer).ToListAsync();
}
}
Frontend code
class="container mt-5">
class="mb-4">Available Cars
class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
@foreach (var car in Model.Cars)
{
class="col">
class="card h-100 shadow-sm">
class="card-body">
class="card-title fs-4">@car.Make @car.Model
class="card-text">
Year: @car.YearOf
Manufacturer: @(car.Manufacturer?.Name ?? "N/A")
}
Source code
Summary
This article provides a path to seed a database initially with EF Core 9, with easy-to-follow instructions and fully commented source code.