Practical code samples for Advanced Data Seeding Strategies in Entity Framework Core — covering both model-based
HasDataseeding and custom runtime seeders with full idempotency guards.
This repository demonstrates two production-ready EF Core data seeding strategies:
| Strategy | Best For | Pros | Cons |
|---|---|---|---|
Model-based HasData |
Static reference data (lookup tables, roles, categories) | Migrations-aware, zero startup overhead, deterministic | Hard-coded IDs required; large datasets inflate migration files |
| Custom Runtime Seeders | Dynamic/computed data, large datasets, external source data | Full C# expressiveness, no migration bloat, DB-generated IDs | Runs at startup, must guard against duplicates |
efcore-data-seeding-seeddata/
├── src/
│ └── EFCore.DataSeeding.Api/
│ ├── Controllers/
│ │ └── ProductsController.cs # REST endpoints to verify seeded data
│ ├── Data/
│ │ ├── AppDbContext.cs # DbContext with HasData configuration
│ │ └── Seeding/
│ │ ├── IDataSeeder.cs # Seeder marker interface
│ │ ├── DatabaseSeeder.cs # Orchestrates all seeders in order
│ │ └── ProductSeeder.cs # Custom runtime seeder (Strategy 2)
│ ├── Models/
│ │ ├── Category.cs
│ │ └── Product.cs
│ ├── Repositories/
│ │ ├── IProductRepository.cs
│ │ └── ProductRepository.cs # Repository pattern with AsNoTracking
│ └── Program.cs # DI wiring + startup seeding
└── tests/
└── EFCore.DataSeeding.Tests/
├── HasDataSeedingTests.cs # Tests for Strategy 1
└── ProductSeederTests.cs # Tests for Strategy 2 (idempotency verified)
- .NET 10 SDK (or .NET 8/9 — update
TargetFrameworkin.csproj)
git clone https://github.com/codingdroplets/efcore-data-seeding-seeddata.git
cd efcore-data-seeding-seeddata
dotnet run --project src/EFCore.DataSeeding.ApiOpen Swagger UI: https://localhost:{port}/swagger
dotnet test -c ReleaseConfigured inside OnModelCreating in AppDbContext.cs. EF Core applies this data through migrations (or EnsureCreated for InMemory).
modelBuilder.Entity<Category>().HasData(
new Category { Id = 1, Slug = "electronics", Name = "Electronics" },
new Category { Id = 2, Slug = "books", Name = "Books" },
new Category { Id = 3, Slug = "clothing", Name = "Clothing" }
);
⚠️ Important:HasDatarequires hard-coded primary keys. Shadow properties and navigation properties are not supported — use flat scalar values only.
ProductSeeder implements IDataSeeder and is called at startup by DatabaseSeeder. It includes an idempotency guard so it is safe to run on every startup.
public async Task SeedAsync(CancellationToken cancellationToken = default)
{
// Guard: skip if already seeded
bool alreadySeeded = await _db.Products
.AnyAsync(p => p.Stock > 500, cancellationToken);
if (alreadySeeded) return;
// Fetch FK by slug — no hard-coded IDs
var electronics = await _db.Categories
.FirstAsync(c => c.Slug == "electronics", cancellationToken);
await _db.Products.AddRangeAsync(new List<Product>
{
new() { Name = "USB-C Hub (7-in-1)", Price = 49.99m, Stock = 750, CategoryId = electronics.Id },
// ...
}, cancellationToken);
await _db.SaveChangesAsync(cancellationToken);
}In Program.cs:
builder.Services.AddScoped<IDataSeeder, ProductSeeder>();
builder.Services.AddScoped<DatabaseSeeder>();Add more seeders by implementing IDataSeeder and registering them. Control execution order via the Order property.
The sample uses InMemory for zero-friction local runs. To use SQL Server:
-
Update
Program.cs:builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
-
Replace
EnsureCreatedAsync()with:await db.Database.MigrateAsync();
-
Add your connection string to
appsettings.json.
| Test | Strategy |
|---|---|
HasData_Seeds_ThreeCategories |
Strategy 1 |
HasData_Seeds_ElectronicsCategory_WithCorrectSlug |
Strategy 1 |
HasData_Seeds_FourBaseProducts |
Strategy 1 |
HasData_Products_HaveCorrectCategoryAssignments |
Strategy 1 |
ProductSeeder_InsertsHighStockProducts |
Strategy 2 |
ProductSeeder_IsIdempotent_DoesNotDuplicate |
Strategy 2 |
ProductSeeder_AssignsCorrectCategories |
Strategy 2 |
Visit Now: https://codingdroplets.com
Join our Patreon to Learn & Level Up: https://www.patreon.com/CodingDroplets
MIT © Coding Droplets