ASP - Core Migrate EF Core SQL DB on Startup

后端 未结 11 889
故里飘歌
故里飘歌 2020-12-01 02:32

Is it possible to have my ASP Core Web API ensure the DB is migrated to the latest migration using EF Core? I know this can be done through the command line, but I want to

相关标签:
11条回答
  • 2020-12-01 03:17

    I followed the IStartupFilter approach to have a generic way for migrating any context.

     public class DataContextAutomaticMigrationStartupFilter<T> : IStartupFilter
      where T : DbContext
    {
        /// <inheritdoc />
        public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
        {
            return app =>
            {
                using (var scope = app.ApplicationServices.CreateScope())
                {
                    scope.ServiceProvider.GetRequiredService<T>().Database.SetCommandTimeout(160);
                    scope.ServiceProvider.GetRequiredService<T>().Database.Migrate();
                }
                next(app);
            };
        }
    }
    

    Now we're able to register the DataContexts and migration in the following way:

    1st context

     services.AddDbContext<ConsumerDataContext>(options => options.UseSqlServer(configuration.GetConnectionString("ConsumerConnection")), ServiceLifetime.Transient);
        services.AddTransient<IStartupFilter, DataContextAutomaticMigrationStartupFilter<ConsumerDataContext>>();
    

    2nd context

    services.AddDbContext<UserDataContext>(options => options.UseSqlServer(configuration.GetConnectionString("UserConnection")), ServiceLifetime.Transient);
    services.AddTransient<IStartupFilter, DataContextAutomaticMigrationStartupFilter<UserDataContext>>();
    

    ..and so on..

    The culprit of IStartupFilter is that it only allows synchronous execution of code. For database migrations this is not an issue since we have a synchronous Migrate() method.

    0 讨论(0)
  • 2020-12-01 03:20

    Starting .NET Core 2 using C# 7.1, you can have an asynchronous Main method to your app, so you can call all initialization logic before you run the host, right after it has finished building:

    public class Program
    {
      public static async Task Main(string[] args)
      {
        //first build
        var host = CreateHostBuilder(args).Build();
    
        //initialize
        using (var serviceScope = host.Services.CreateScope())
        {
          var serviceProvider = serviceScope.ServiceProvider;
          var isDevelopment = 
            serviceProvider.GetRequiredService<IWebHostEnvironment>().IsDevelopment();
    
          using var context = serviceProvider.GetRequiredService<AppDbContext>();
    
    
          if (isDevelopment)
            await context.Database.EnsureCreatedAsync();
          else
            await context.Database.MigrateAsync();
    
          if (isDevelopment)
          {
            using var userManager = 
              serviceProvider.GetRequiredService<UserManager<AppUser>>();
            await userManager
              .CreateAsync(new AppUser { UserName = "dummy", Email = "dummy@dumail.com" },
              password: "1234");
          }
        }
    
        //now run
        host.Run();
      }
    
      public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
          .ConfigureWebHostDefaults(webBuilder =>
          {
            webBuilder.UseStartup<Startup>();
          });
    }
    
    0 讨论(0)
  • 2020-12-01 03:22

    A note from documentation on the call to db.Database.EnsureCreated():

    Note that this API does not use migrations to create the database. In addition, the database that is created cannot be later updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate() method to ensure the database is created and all migrations are applied.

    You may just want to call db.Database.Migrate().

    Comment taken from source found above declaration here.

    0 讨论(0)
  • 2020-12-01 03:25

    This is a slight correction to the previous answer which created an extension method. It fixes the error that is thrown the way it was written.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MyApp.Extensions
    {
        public static class IApplicationBuilderExtensions
        {
            public static void SyncMigrations<T>(this IApplicationBuilder app) where T : DbContext
            {
                using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
                {
                    var context = serviceScope.ServiceProvider.GetService<DbContext>();
                    context.Database.Migrate();
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 03:26

    This works for me in ASP.NET Core 3.1, simply injecting the db context as a parameter to the Configure method after registering it in the ConfigureServices method.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<DataContext>(x => x.UseSqlite("Data Source=LocalDatabase.db"));
    
        ...
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
    {
        dataContext.Database.Migrate();
    
        ...
    }
    

    More details and links to full code samples available at https://jasonwatmore.com/post/2019/12/27/aspnet-core-automatic-ef-core-migrations-to-sql-database-on-startup

    0 讨论(0)
提交回复
热议问题