15

In .Net 5, we use to be able to call the migration by passing DataContext to Configure method and call the migration in startup class.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
    // migrate any database changes on startup (includes initial db creation)
    dataContext.Database.Migrate();

    ...
}

How can we do it in .Net 6?

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
fereshteh rabet
  • 198
  • 1
  • 2
  • 11
  • Not sure what you are talking about, doing migrations and seeding in Startup have been deprecated since .NET Core 2.0 was released 2017. Simple search would have helped https://stackoverflow.com/a/45942026/455493. May be slightly outdated and methods changed slightly, the general idea is the same – Tseng Dec 07 '21 at 22:34
  • What's the question? [Applying Migrations at Runtime](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=vs#apply-migrations-at-runtime) shows that `dbContext.Database.Migrate()` hasn't changed. Are you asking where to put the code that used to be inside `Startup.Configure`? – Panagiotis Kanavos Dec 08 '21 at 07:24
  • BTW thanks for helping me clarify the change from Startup.cs to minimal APIs in my mind. Up to now I've been writing the correct code mainly by checking which class has the properties I needed. That's the first time it actually clicked what goes where and why. – Panagiotis Kanavos Dec 08 '21 at 13:40

3 Answers3

37

Short Version

It sounds like the real question is where to put code that used to live in Startup.Configure.

In Program.cs use

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
    db.Database.Migrate();
}

Rather long explanation

The Applying Migrations at Runtime section in the EF Core Migrations docs shows that nothing's changed as far as EF Core is concerned.

public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
        //Same as the question
        db.Database.Migrate();
    }

    host.Run();
}

It sounds like the real question is where to put code that used to live in Startup.Configure. That code can be placed in the Main method or, if Minimal APIs are used, inside Program.cs. Configuration, Services, Environment etc are available as properties in the WebApplicationBuilder class or the WebApplication created by it. WebApplicationBuilder contains the builder interfaces for DI, configuration, Logging and the host, eg WebApplicationBuilder.Services exposes IServiceCollection.

WebApplication properties expose the middleware configured by WebApplicationBuilder, eg WebApplication.Services exposes IServiceProvider

Startup replacement in Minimal APIs

The methods that were in Startup.cswere merged in Program.cs in .NET 6. Startup.cs contained two kinds of methods:

  • Methods to configure the host and application, like setting up configuration and DI, by calling the various builder interfaces like IServiceCollection, IConfigurationBuilder. This includes the code that used to be in Startup.ConfigureServices.
  • Methods that used the host to configure endpoints, use services and middleware. This includes code that was in Startup.Configure.

In .NET 6, the interfaces move to the WebApplicationBuilder and WebApplication classes. Instead of .NET Core calling a "magic" Startup class and injecting the interfaces, the code in Program.cs can access the interfaces it needs directly.

  • The host building/configuration services are now available through the WebApplicationBuilder class.
  • Interfaces provided by the complete application host are now available through the WebApplication class which is built by the WebApplicationBuilder.

If you don't need to configure services, you can create a minimal API application with just 3 lines :

var app = WebApplication.Create(args);

app.MapGet("/", () => "Hello World!");

app.Run();

In your case you need to configure the DbContext at least, so you need to use WebApplicationBuilder and WebApplication separately. This is shown in the next section

Migrations in Minimal APIs

In the basic minimal API Program.cs :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

DbContexts can be created once a WebApplication instance is created through its Services property:

var builder = WebApplication.CreateBuilder(args);
//Register the DbContexts etc.
...
builder.Services.AddDbContext<SomeDbContext>(....);

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
    db.Database.Migrate();
}

app.MapGet("/", () => "Hello World!");

app.Run();

Of course it's a lot better to use separate methods or classes for such code, keeping Program.cs clean :

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();

ApplyMigrations(app);

app.MapGet("/", () => "Hello World!");

app.Run();

static void ApplyMigrations(WebApplication app)
{
    using var scope = app.Services.CreateScope();
    var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
    db.Database.Migrate();
}

Or even :

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();

app.ApplyMigrations()
   .UseCustomLogging()
   .DoSomeOtherConfiguration()
   ...;

app.MapGet("/", () => "Hello World!");

app.Run();

With ApplyMigrations an extension method in a separate class :

public static DataExtensions
{
    public static WebApplication ApplyMigrations(this WebApplication app)
    {
        using var scope = app.Services.CreateScope()
        var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
        db.Database.Migrate();
        return app;
    }
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
9

In ASP.NET Core 6, it should be:

using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
     
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
    db.Database.Migrate();
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Rena
  • 30,832
  • 6
  • 37
  • 72
  • Code-only answers are acceptable only if they're obvious. This one isn't. The OP wouldn't post a question if they knew how to retrieve an instance of their DbContext, or what replaces the `Startup` methods – Panagiotis Kanavos Dec 08 '21 at 09:39
0

You can use this line dbContext.Database.GetPendingMigrations().Any() to check Pending Migrations

use it before dbContext.Database.Migrate();

I use the following code and it works fine for me


using (var scope = builder.Services.BuildServiceProvider().CreateScope())
{
    using(var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>())
    {
        if (dbContext.Database.GetPendingMigrations().Any())
        {
            dbContext.Database.Migrate();
        }
    }
}
Akram Al-Qaifi
  • 386
  • 1
  • 11