entity-framework-coreasp.net-core-webapief-core-3.0

Where to call EF Core Migrate in web API startup class?


I'm trying to process DB migrations during application startup in a Azure-hosted web API. However, no matter where I try to put it, I get the exception:

ObjectDisposedException: Cannot access a disposed object.

I gather that this is because I'm wrapping the context Migrate call in a "using" statement during the wrong part of the injection process. But I'm not sure where it should be located.

I've consulted the following articles:

And have tried the following migration locations:

public Startup(IConfiguration appSettings)
{
  using var dbContext = new MyContext();
  dbContext.Database.Migrate();
}

This produced the ObjectDisposedException on MyContext. So I tried removing it from the constructor and adding it to services and injecting it into the configure call (per the first and second articles):

public Startup(IConfiguration appSettings)
{
}
public void ConfigureServices(IServiceCollection services)
{
  services.AddDbContext<MyContext>();
  [...]
}
public void Configure(IApplicationBuilder app, 
  IWebHostEnvironment env, MyContext dbcontext)
{
  [...]
  dbcontext.Database.Migrate();
}

This also produced the ObjectDisposedException on MyContext. So I tried changing how the configure method access the context service (per the third article):

public Startup(IConfiguration appsettings)
{
}
public void ConfigureServices(IServiceCollection services)
{
  services.AddDbContext<MyContext>();
  [...]
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  [...]
  using var serviceScope = app.ApplicationServices
    .GetRequiredService<IServiceScopeFactory>()
    .CreateScope();
  using var context = serviceScope.ServiceProvider
    .GetService<MyDbContext>();
  context.Database.Migrate();
}

Which still produced the ObjectDisposedException on MyContext.

I would appreciate any help in understanding what I'm doing wrong trying to implement this EF Core migration! Thanks!


Solution

  • If you understand the security risks of using a connection string with permissions to alter the database schema and you understand problems that can occur when multiple instances of your app try to migrate the database at the same time...

    You can do it in Program.Main. If you're using Microsoft.Extensions.Hosting, it would look like this:

    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
    
        using (var scope = host.Services.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            db.Database.Migrate();
        }
    
        host.Run();
    }
    

    You could also consider adding an admin page that requires the user to be authenticated before clicking a button to migrate the database.