如何以及在何处调用Database.EnsureCreated和Database.Migrate?


78

我有一个ASP.NET MVC 6应用程序,我需要调用Database.EnsureCreatedDatabase.Migrate方法。

但是我应该在哪里打电话呢?


您可能不想使用任何一个。MS文档说了有关使用Migrate()的观点:“虽然非常适合具有本地数据库的应用程序,但大多数应用程序将需要更强大的部署策略,例如生成SQL脚本。” docs.microsoft.com/en-us/ef/core/managing-schemas/migrations
John Pankowicz,

Answers:


94

我认为这是一个重要的问题,应该得到很好的回答!

什么是Database.EnsureCreated?

context.Database.EnsureCreated()是一种新的EF核心方法,可确保上下文数据库存在。如果存在,则不采取任何措施。如果它不存在,那么将创建数据库及其所有架构,并确保该数据库与该上下文的模型兼容。

注意: 此方法不使用迁移来创建数据库。此外,创建的数据库以后无法使用迁移进行更新。如果您以关系数据库为目标并使用迁移,则可以使用该DbContext.Database.Migrate()方法来确保创建数据库并应用所有迁移。

我们如何使用EF 6做到这一点?

context.Database.EnsureCreated() 等效于下面列出的EF 6方法:

  1. 程序包管理器控制台:

    Enable-Migrations -EnableAutomaticMigrations。添加迁移/更新数据库。

  2. 来自代码:

    Database.SetInitializer CreateDatabaseIfNotExists

要么

使用DbMigrationsConfiguration并设置AutomaticMigrationsEnabled = true;

什么是Database.Migrate?

将上下文的所有未决迁移应用到数据库。如果尚不存在,将创建数据库。

我们如何使用EF 6做到这一点?

context.Database.Migrate() 等效于下面列出的EF 6方法:

  1. 程序包管理器控制台:

    更新数据库-TargetMigration

  2. 使用自定义DbMigrationsConfiguration:

    AutomaticMigrationsEnabled = false; 或使用DbMigrator。

结论

如果您正在使用迁移,则有context.Database.Migrate()。如果您不希望迁移,而只是想要一个快速数据库(通常用于测试),则可以使用context.Database.EnsureCreated()/ EnsureDeleted()。


2
您好Bassam Alugili,谢谢您的回答!在我的项目中,我正在使用迁移,但我不知道一个人不应该同时使用这两种方法。
bailando bailando 2016年

1
uw,这是一个如何调用它的示例!stefanhendriks.com/2016/04/29/...
巴萨姆Alugili

1
我当时在考虑创建Database.Migrate()迁移(如果需要),然后根据其更新基础。类似于EF 6中的自动迁移。但是我错了。它仅在数据库上应用现有迁移(如果有)。
Afshar Mohebi

据我了解,Database.Migrate使用与应用程序在执行插入/查询等数据库时相同的数据库信用。我们是否希望这些操作由具有创建/删除特权的用户执行?这有没有办法让Database.Migrate()使用其他贷方(具有创建/删除特权)?
阿吉尔·贡纳尔·斯特凡森(Asgeir GunnarStefánsson)

2
您刚刚救了我免于未来的灾难。荣誉
Shaswat Rungta

24

有了James P和Bassam Alugili提供的信息,我最终要做的是将这些代码行添加到类(Startup.cs)中的Configure方法中:Startup

using (var scope = 
  app.ApplicationServices.CreateScope())
using (var context = scope.ServiceProvider.GetService<MyDbContext>())
    context.Database.Migrate();

这正是我想要的。大多数示例都使用.Net核心或Web,而我使用的是.Net 4.6的Windows Forms应用程序。数据库已创建(因为连接字符串中的用户无权创建数据库)。上面的代码创建了所有表以及迁移中的所有内容。
nivs1978 '19

16

正如foreward你应该阅读从罗恩·米勒:

...EnsureCreated完全绕开了迁移,只是为您创建了架构,您不能将其与迁移混在一起。EnsureCreated专为测试或快速原型设计,您可以每次删除并重新创建数据库。如果您正在使用迁移,并希望在应用启动时自动应用它们,则可以context.Database.Migrate()改用。

根据这里的答案您需要将Globals.EnsureDatabaseCreated();其添加到Startup.cs

Startup.cs中的启动功能:

public Startup(IHostingEnvironment env)
{
    // Set up configuration sources.
    var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddEnvironmentVariables();

    if (env.IsDevelopment())
    {
        // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
            builder.AddApplicationInsightsSettings(developerMode: true);
    }
    Configuration = builder.Build();
    Globals.Configuration = Configuration;
    Globals.HostingEnvironment = env;
    Globals.EnsureDatabaseCreated();
}

并定义Globals.EnsureDatabaseCreated()如下:

public static void EnsureDatabaseCreated()
    {
        var optionsBuilder = new DbContextOptionsBuilder();
        if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:DataContext"]);
        else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:DataContext"]);
        else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:DataContext"]);
        var context = new ApplicationContext(optionsBuilder.Options);
        context.Database.EnsureCreated();

        optionsBuilder = new DbContextOptionsBuilder();
        if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:TransientContext"]);
        else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:TransientContext"]);
        else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:TransientContext"]);
        new TransientContext(optionsBuilder.Options).Database.EnsureCreated();
    }

要使用context.Database.Migrate()请参阅此处此处


詹姆斯,您好,谢谢您的回答!,我在启动方法中无法访问名称可变的Globals,我该如何访问?
bailando bailando

2
一样,没有看到Globals。这看起来像是尝试撬起的非标准方式
Douglas Gaskell

据我了解,标准方法是在Startup.ConfigureServices中创建DbContext,但是通过某种间接方法创建。您可以将其钓回那里,也可以在Startup.Config中使用app.ApplicationServices.GetRequiredService <T>进行配置。我认为。
乔什·萨特菲尔德

6

通常,DbContext将这样添加到依赖项注入容器中Startup.ConfigureServices()

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add DbContext to the injection container
        services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer(
                    this.Configuration.GetConnectionString("DefaultConnection")));
    }
}

但是,IServiceCollection不会充当服务提供者,并且由于DbContext尚未在当前作用域()之前在注入容器中注册,因此Startup.ConfigureServices我们无法在此处通过依赖项注入访问上下文。

Henk Mollema在此处讨论了启动过程中的手动解决服务,但提到了...

手动解决服务(又称为服务定位器)通常被认为是一种反模式... [并且]您应尽可能避免使用它。

Henk还提到,Startup构造函数的依赖项注入非常有限,并且不包含在中配置的服务Startup.ConfigureServices(),因此DbContext的用法最简单,并且最适合在应用程序其余部分中使用的注入容器。

运行时的托管服务提供程序可以将某些服务注入到Startup类的构造函数中,例如IConfigurationIWebHostEnvironmentIHostingEnvironment在3.0之前的版本中)ILoggerFactoryIServiceProvider。请注意,后者是由托管层构建的实例,并且仅包含用于启动应用程序的基本服务。

为了调用Database.EnsureCreated()Database.Migrate(),我们可以并且希望在中自动解析DbContext Startup.Configure(),现在可以通过DI使用我们已配置的服务:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add DbContext to the injection container
        services.AddDbContext<MyDbContext>(options =>
                options.UseSqlServer(
                    this.Configuration.GetConnectionString("DefaultConnection")));
    }

    public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
    {
        if (env.IsDevelopment())
        {
            context.Database.EnsureCreated();
            //context.Database.Migrate();
        }
    }
}

请记住,EF核心文档中引用了Bassam Alugili的答案Database.EnsureCreated()Database.Migrate()但不能一起使用,因为这样可以确保将现有迁移应用于需要创建的数据库。另一个只是确保数据库存在,如果不存在,则创建一个反映您的数据库DbContext,包括通过上下文中的Fluent API完成的任何种子设置。


1

另外,如果您在上下文的构造函数中调用此函数,则可能会EnsureCreated导致性能下降。转移到setup.cs实用程序后,我注意到响应时间有了很大的改进。

注意:我正在使用EFC和UWP。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.