A powerful, flexible, and dependency-aware database seeding framework for .NET applications.
Database seeding is the process of populating a database with initial data. This could be:
- Test data for development
- Default configuration values
- Required lookup tables
- Sample data for demonstrations
- Initial user accounts and roles
- And more!
While seeding might sound simple, it can become complex when dealing with:
- Dependencies between different types of data
- Proper ordering of data insertion
- Handling relationships between entities
- Managing large datasets
- Maintaining data consistency
- Different environments (development, testing, production)
This is where DatabaseSeeder comes in!
π Core Features
- Dependency-aware seeding with automatic ordering
- Support for parallel seeding execution
- Transaction management
- Batched data operations for large datasets
- Flexible data validation
- Comprehensive logging
π οΈ Technical Features
- Entity Framework Core integration
- JSON data source support
- Custom data provider extensibility
- Configurable execution options
- Dependency injection support
- Asynchronous operations
π§ Developer Experience
- Fluent configuration API
- Clear separation of concerns
- Easy to extend and customize
- Comprehensive testing support
- Rich logging and diagnostics
dotnet add package DatabaseSeeder
dotnet add package DatabaseSeeder.EntityFramework # If using Entity Framework
dotnet add package DatabaseSeeder.Json # If using JSON data sourcespublic class CategorySeeder : BaseSeeder
{
private readonly MyDbContext _dbContext;
private readonly IDataSeeder<Category> _dataSeeder;
public CategorySeeder(
MyDbContext dbContext,
IDataSeeder<Category> dataSeeder,
ILogger<CategorySeeder> logger) : base(logger)
{
_dbContext = dbContext;
_dataSeeder = dataSeeder;
}
public override int Order => 1; // Lower numbers run first
public override async Task SeedAsync(CancellationToken cancellationToken = default)
{
var categories = await _dataSeeder.GetSeedDataAsync(cancellationToken);
await _dbContext.Categories.AddRangeAsync(categories, cancellationToken);
await _dbContext.SaveChangesAsync(cancellationToken);
}
}public void ConfigureServices(IServiceCollection services)
{
services.AddDatabaseSeeder()
.Configure(options =>
{
options.EnableParallelization = true;
options.MaxDegreeOfParallelization = 4;
})
.AddSeeder<CategorySeeder>()
.AddSeeder<ProductSeeder>()
.AddDataSeeder<Category, CategoryDataSeeder>()
.Build();
}public async Task SeedDatabase(IServiceProvider services)
{
var orchestrator = services.GetRequiredService<ISeederOrchestrator>();
await orchestrator.SeedAllAsync();
}Seeders are classes that handle the actual data insertion into your database. They can:
- Define dependencies on other seeders
- Specify execution order
- Handle transaction management
- Implement custom validation logic
Data seeders provide the actual data to be seeded. They:
- Can read from various sources (JSON, CSV, hardcoded data, etc.)
- Can implement custom data transformation
- Control whether existing data should be cleaned
- Handle data validation
The orchestrator manages the seeding process by:
- Resolving dependencies between seeders
- Managing execution order
- Handling parallel execution
- Managing transactions
- Providing error handling and logging
DatabaseSeeder automatically handles dependencies between seeders:
public class ProductSeeder : BaseSeeder
{
public override int Order => 2;
public override IEnumerable<Type> Dependencies =>
new[] { typeof(CategorySeeder) }; // Products depend on Categories
}Enable parallel seeding for better performance:
services.AddDatabaseSeeder()
.Configure(options =>
{
options.EnableParallelization = true;
options.MaxDegreeOfParallelization = 4;
options.ContinueOnError = false;
})
.Build();Control transaction behavior:
public class EntityFrameworkSeederOptions<TEntity>
{
public bool UseTransactions { get; set; } = true;
public bool UseBatching { get; set; } = true;
public int BatchSize { get; set; } = 1000;
}Easily seed from JSON files:
services.AddDatabaseSeeder()
.AddJsonDataProvider<Category>("categories.json", options =>
{
options.BaseDirectory = "SeedData";
options.ValidateSchema = true;
})
.Build();-
Order Matters: Use the
Orderproperty to control execution sequence. Lower numbers run first. -
Clear Dependencies: Always explicitly declare seeder dependencies using the
Dependenciesproperty. -
Transaction Control: Use transactions for related data to maintain consistency.
-
Batch Operations: Enable batching for large datasets to improve performance.
-
Validation: Implement proper validation in your seeders to ensure data integrity.
-
Error Handling: Configure appropriate error handling strategies based on your needs.
public override async Task SeedAsync(CancellationToken cancellationToken)
{
if (_dataSeeder.ShouldCleanExistingData)
{
await CleanExistingDataAsync(cancellationToken);
}
var entities = await _dataSeeder.GetSeedDataAsync(cancellationToken);
await SeedEntitiesAsync(entities, cancellationToken);
}protected virtual async Task<IEnumerable<TEntity>> ValidateEntitiesAsync(
IEnumerable<TEntity> entities,
CancellationToken cancellationToken)
{
var validEntities = new List<TEntity>();
foreach (var entity in entities)
{
if (await ValidateEntityAsync(entity, cancellationToken))
{
validEntities.Add(entity);
}
}
return validEntities;
}We welcome contributions! Please see our Contributing Guidelines for details.
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation
- π Issue Tracker
Built with β€οΈ by the .NET community