RepletoryLib.Data.Migrations 1.0.0

dotnet add package RepletoryLib.Data.Migrations --version 1.0.0
                    
NuGet\Install-Package RepletoryLib.Data.Migrations -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RepletoryLib.Data.Migrations" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RepletoryLib.Data.Migrations" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="RepletoryLib.Data.Migrations" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RepletoryLib.Data.Migrations --version 1.0.0
                    
#r "nuget: RepletoryLib.Data.Migrations, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RepletoryLib.Data.Migrations@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RepletoryLib.Data.Migrations&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=RepletoryLib.Data.Migrations&version=1.0.0
                    
Install as a Cake Tool

RepletoryLib.Data.Migrations

EF Core migration runner and data seeding for automated database setup on application startup.

Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.

NuGet .NET 10 License: MIT


Overview

RepletoryLib.Data.Migrations automates database schema management and seed data provisioning for EF Core applications. It runs pending migrations on application startup, executes data seeders in a defined order, and provides visibility into applied and pending migrations.

The migration runner includes retry logic for transient database failures and can be configured to run or skip migrations at startup.

Key Features

  • Automatic migration on startup -- Apply pending EF Core migrations when the application starts
  • Data seeding -- IDataSeeder interface with ordered execution for idempotent seed data
  • Hosted service -- MigrationHostedService integrates seamlessly with ASP.NET Core hosting
  • Migration visibility -- Query applied and pending migrations programmatically
  • Retry logic -- Built-in retry for transient database failures during migration
  • Configurable -- Enable/disable migration and seeding via configuration

Installation

dotnet add package RepletoryLib.Data.Migrations

Or add to your .csproj:

<PackageReference Include="RepletoryLib.Data.Migrations" Version="1.0.0" />

Note: RepletoryLib packages are published to a local BaGet feed. See the main repository README for feed configuration.

Dependencies

Package Type
RepletoryLib.Common RepletoryLib
Microsoft.EntityFrameworkCore.Relational NuGet (10.0.0)
Microsoft.Extensions.Hosting.Abstractions NuGet (10.0.0)

Quick Start

using RepletoryLib.Data.Migrations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddRepletoryMigrations<AppDbContext>(builder.Configuration);
{
  "RepletoryMigrations": {
    "RunOnStartup": true,
    "SeedAfterMigration": true,
    "TimeoutSeconds": 120
  }
}

When the application starts, MigrationHostedService will automatically apply pending migrations and run registered seeders.

Configuration

MigrationOptions

Property Type Default Description
RunOnStartup bool true Apply pending migrations when the app starts
SeedAfterMigration bool true Run data seeders after migrations complete
TimeoutSeconds int 120 Migration command timeout in seconds

Section name: "RepletoryMigrations"


Usage Examples

Creating a Data Seeder

Implement IDataSeeder to provision seed data. The Order property controls execution order (lower runs first).

using RepletoryLib.Data.Migrations.Interfaces;

public class RoleSeeder : IDataSeeder
{
    public int Order => 1; // Runs first

    public async Task SeedAsync(IServiceProvider serviceProvider, CancellationToken ct = default)
    {
        var context = serviceProvider.GetRequiredService<AppDbContext>();

        if (await context.Roles.AnyAsync(ct))
            return; // Already seeded -- idempotent

        context.Roles.AddRange(
            new Role { Name = "Admin", Description = "Full access" },
            new Role { Name = "User", Description = "Standard user" },
            new Role { Name = "ReadOnly", Description = "View only" }
        );

        await context.SaveChangesAsync(ct);
    }
}

public class DefaultAdminSeeder : IDataSeeder
{
    public int Order => 2; // Runs after RoleSeeder

    public async Task SeedAsync(IServiceProvider serviceProvider, CancellationToken ct = default)
    {
        var context = serviceProvider.GetRequiredService<AppDbContext>();

        if (await context.Users.AnyAsync(u => u.Email == "admin@example.com", ct))
            return;

        var adminRole = await context.Roles.FirstAsync(r => r.Name == "Admin", ct);
        context.Users.Add(new User
        {
            Email = "admin@example.com",
            FullName = "System Administrator",
            RoleId = adminRole.Id
        });

        await context.SaveChangesAsync(ct);
    }
}

Registering Seeders

builder.Services.AddRepletoryMigrations<AppDbContext>(builder.Configuration);

// Register seeders (they'll be discovered and executed in Order)
builder.Services.AddTransient<IDataSeeder, RoleSeeder>();
builder.Services.AddTransient<IDataSeeder, DefaultAdminSeeder>();

Querying Migration Status

using RepletoryLib.Data.Migrations.Interfaces;

public class DatabaseStatusController : ControllerBase
{
    private readonly IMigrationRunner _runner;

    public DatabaseStatusController(IMigrationRunner runner) => _runner = runner;

    [HttpGet("db/status")]
    public async Task<IActionResult> GetStatus()
    {
        var applied = await _runner.GetAppliedMigrationsAsync();
        var pending = await _runner.GetPendingMigrationsAsync();

        return Ok(new
        {
            AppliedMigrations = applied,
            PendingMigrations = pending,
            IsUpToDate = pending.Count == 0
        });
    }
}

Running Migrations Manually

If you disable RunOnStartup, you can trigger migrations manually:

builder.Services.AddRepletoryMigrations<AppDbContext>(builder.Configuration);

var app = builder.Build();

// Manually run migrations (e.g., from a CLI command or admin endpoint)
using var scope = app.Services.CreateScope();
var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
await runner.MigrateAsync();

API Reference

IMigrationRunner

Method Returns Description
MigrateAsync(ct) -- Apply all pending migrations with retry logic
GetAppliedMigrationsAsync(ct) IReadOnlyList<string> List of already-applied migration names
GetPendingMigrationsAsync(ct) IReadOnlyList<string> List of migrations waiting to be applied

IDataSeeder

Member Type Description
Order int Execution priority (lower = earlier)
SeedAsync(serviceProvider, ct) Task Seed data idempotently

Integration with Other RepletoryLib Packages

Package Relationship
RepletoryLib.Common Direct dependency
RepletoryLib.Data.EntityFramework Shares DbContext; use migrations for schema, EF for runtime access
RepletoryLib.HealthChecks Check migration status as a health indicator
RepletoryLib.Logging Migration progress logged via Serilog

Testing

Test seeders in isolation using InMemoryDbContextFactory:

[Fact]
public async Task RoleSeeder_creates_default_roles()
{
    var factory = new InMemoryDbContextFactory<AppDbContext>();
    var context = factory.Create();
    var services = new ServiceCollection()
        .AddSingleton(context)
        .BuildServiceProvider();

    var seeder = new RoleSeeder();
    await seeder.SeedAsync(services);

    context.Roles.Should().HaveCount(3);
}

[Fact]
public async Task RoleSeeder_is_idempotent()
{
    var factory = new InMemoryDbContextFactory<AppDbContext>();
    var context = factory.Create();
    var services = new ServiceCollection()
        .AddSingleton(context)
        .BuildServiceProvider();

    var seeder = new RoleSeeder();
    await seeder.SeedAsync(services);
    await seeder.SeedAsync(services); // Run twice

    context.Roles.Should().HaveCount(3); // Still 3
}

Troubleshooting

Issue Solution
Migrations fail on startup Check database connectivity and credentials. The runner retries transient failures, but permanent errors will throw.
Seeders run in wrong order Verify the Order property values. Lower values run first.
Seeders insert duplicates Ensure seeders check for existing data before inserting (idempotent pattern)
Timeout during migration Increase TimeoutSeconds in MigrationOptions for large schema changes

License

This project is licensed under the MIT License.

Copyright (c) 2024-2026 Repletory.


For complete documentation, infrastructure setup, and configuration reference, see the RepletoryLib main repository.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 149 3/2/2026