ASP - 核心在启动时迁移EF Core SQL DB

时间:2016-06-12 23:34:36

标签: c# asp.net-core entity-framework-core

是否可以让我的ASP Core Web API确保使用EF Core将数据库迁移到最新的迁移?我知道这可以通过命令行完成,但我想以编程方式完成。

更新

根据Janshair Khan的回答,我提出了这个助手类:

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using MyWebApi.Models;

namespace MyWebApi
{
    public static class DataSeeder
    {
        public static void SeedData(this IApplicationBuilder app)
        {
            var context = app.ApplicationServices.GetService<MyContext>();

            if (!context.Database.EnsureCreated())
                context.Database.Migrate();
        }
    }
}

您可以通过Configure中的Startup.cs方式拨打此电话:

app.SeedData();

11 个答案:

答案 0 :(得分:34)

有关致电db.Database.EnsureCreated()的文档的说明:

  

请注意,此API不使用迁移来创建数据库。在   此外,创建的数据库以后无法使用更新   迁移。如果您要定位关系数据库并使用   迁移时,可以使用DbContext.Database.Migrate()方法   确保创建数据库并应用所有迁移。

您可能只想致电db.Database.Migrate()

来自上述声明here的来源的评论。

答案 1 :(得分:25)

您可以使用

db.Database.EnsureCreated();

让您的数据库与您当前的模型保持同步。如果要启用迁移(如果怀疑后续迁移),请使用

db.Database.Migrate();

随着时间的推移进行后续迁移。

答案 2 :(得分:12)

使用以下代码在

运行迁移
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
    {
        var context = serviceScope.ServiceProvider.GetService<YourContext`enter code here`>();
        context.Database.Migrate();
    }
}

答案 3 :(得分:7)

基于@steamrolla的回答,我建议进行以下改进:

public static class EnsureMigration
{
    public static void EnsureMigrationOfContext<T>(this IApplicationBuilder app) where T:DbContext
    {
        var context = app.ApplicationServices.GetService<T>();
        context.Database.Migrate();
    }
}

通过这种方式,您还可以确保迁移不同的上下文,例如如果你有一个身份数据库。

用法:

app.EnsureMigrationOfContext<context>();

答案 4 :(得分:1)

根据chintan310的回答,以下是我迁移数据库的方法。这可确保将与数据库相关的任务分离为Program.cs

    public static void Main(string[] args)
    {
        var host = BuildWebHost(args);

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetService<AppDbContext>();
                context.Database.Migrate();

                var seeder = scope.ServiceProvider.GetService<AppSeeder>();
                seeder.Seed().Wait();
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    private static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();

答案 5 :(得分:1)

我这样做是为了根据此处的先前答案以及“ bailando bailando's answer”上的How and where to call Database.EnsureCreated and Database.Migrate?,使用EF Core 2.1.2和SQL Server进行编程迁移:

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;

namespace MyApp
{
    public class Startup
    {
        // ... (only relevant code included) ...

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MyAppContext>(options => 
                options.UseSqlServer(Configuration.GetConnectionString("MyAppContext")));
            // ...
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            using (var serviceScope = app.ApplicationServices.CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetService<MyAppContext>();
                context.Database.Migrate();
            }
            // ...
        }
    }
}

使用此代码的项目是available at Github

答案 6 :(得分:1)

此代码在.NET Core 3.0中有效

 using (var scope = app.ApplicationServices.CreateScope())
 {
     var dbContext = scope.ServiceProvider.GetService<T>();
     dbContext.Database.Migrate();
 }

答案 7 :(得分:1)

我遵循了IStartupFilter的方法,以一种通用的方式来迁移任何上下文。

 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);
        };
    }
}

现在,我们可以通过以下方式注册DataContext和迁移:

第一个上下文

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

第二背景

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

..依此类推。

IStartupFilter的罪魁祸首是它仅允许同步执行代码。对于数据库迁移,这不是问题,因为我们有一个同步Migrate()方法。

答案 8 :(得分:0)

这是对以前创建扩展方法的答案的略微修正。它修复了编写时抛出的错误。

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();
            }
        }
    }
}

答案 9 :(得分:0)

这对我来说在ASP.NET Core 3.1中是有效的,只需将db上下文作为参数注册到Configure方法中之后,即可将其插入到ConfigureServices方法中。

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();

    ...
}

更多详细信息以及指向完整代码示例的链接,网址为https://jasonwatmore.com/post/2019/12/27/aspnet-core-automatic-ef-core-migrations-to-sql-database-on-startup

答案 10 :(得分:0)

使用C#7.1启动.NET Core 2,您可以对应用程序使用异步Main方法,因此可以在主机完成构建后立即在运行主机之前调用所有初始化逻辑:

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>();
      });
}