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

Posted

技术标签:

【中文标题】如何以及在何处调用 Database.EnsureCreated 和 Database.Migrate?【英文标题】:How and where to call Database.EnsureCreated and Database.Migrate? 【发布时间】:2016-11-09 08:00:27 【问题描述】:

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

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

【问题讨论】:

您可能也不想使用。 MS 文档这样说关于使用 Migrate():“虽然它对于具有本地数据库的应用程序来说非常棒,但大多数应用程序将需要更强大的部署策略,例如生成 SQL 脚本。” docs.microsoft.com/en-us/ef/core/managing-schemas/migrations 【参考方案1】:

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

什么是 Database.EnsureCreated?

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

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

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

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

    包管理器控制台:

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

    来自代码:

    Database.SetInitializer CreateDatabaseIfNotExists

使用 DbMigrationsConfiguration 并设置 AutomaticMigrationsEnabled = true;

什么是 Database.Migrate?

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

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

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

    包管理器控制台:

    更新数据库 -TargetMigration

    使用自定义 DbMigrationsConfiguration:

    AutomaticMigrationsEnabled = false;或使用 DbMigrator。

结论

如果您使用迁移,则有 context.Database.Migrate()。如果您不想要迁移并且只想要一个快速数据库(通常用于测试),请使用 context.Database.EnsureCreated()/EnsureDeleted()。

【讨论】:

您好 Bassam Alugili,感谢您的回答!在我的项目中,我正在使用迁移,我不知道不应该同时使用这两种方法。 uw 这是一个如何调用它的例子! stefanhendriks.com/2016/04/29/… 我在想Database.Migrate() 创建迁移(如果需要)然后更新基于它的基础。就像 EF 6 中的自动迁移一样。但我错了。它仅在数据库上应用现有迁移(如果有)。 据我了解,Database.Migrate 在对数据库执行插入/查询等操作时使用应用程序使用的相同数据库信用。我们是否希望这些操作由具有创建/删除权限的用户完成?这有办法让 Database.Migrate() 使用其他信用(具有创建/删除权限)? 你刚刚把我从未来的灾难中救了出来。 【参考方案2】:

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

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

【讨论】:

这正是我想要的。大多数示例使用 .Net 核心或 Web,而我使用的是 .Net 4.6 的 Windows 窗体应用程序。数据库已创建(因为连接字符串中的用户无权创建数据库)。上面的代码创建了迁移中的所有表和所有内容。 对不起,这是我认为与本案有关的问题的链接:***.com/questions/70512142/…。你能看看它,看看你的 sulotion 对我有没有帮助?如果没有,你能给我一些解决问题的方法吗? 在 .NET 6 Startup.cs 中不再存在。我们有关于现在在哪里使用它的更新吗?【参考方案3】:

作为前锋,您应该阅读 Rowan Miller 的 this:

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

根据回答here需要将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(),请参阅here 或here。

【讨论】:

你好 James,谢谢你的回答!,我在我的启动方法中没有任何访问变量名 Globals 的权限,我怎样才能访问它? 同样,没有看到Globals。这看起来像是一种试图在中撬开它的非标准方式 据我了解,标准方式是在 Startup.ConfigureServices 中创建 DbContext,但通过某种间接方法。您可以将其重新放回原处,或在 Startup.Configure 中使用 app.ApplicationServices.GetRequiredService。我想。【参考方案4】:

通常,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 讨论了在启动期间手动解析服务 here,但提到...

手动解析服务(又名服务定位器)是generally considered an anti-pattern ... [并且] 你应该尽可能避免它 尽可能。

Henk 还提到Startup 构造函数的依赖注入非常有限,不包括在Startup.ConfigureServices() 中配置的服务,因此通过在整个应用程序中使用的注入容器,DbContext 的使用是最简单和最合适的。

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

为了调用Database.EnsureCreated()Database.Migrate(),我们可以并且希望在Startup.Configure() 中自动解析 DbContext,我们配置的服务现在可以通过 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();
        
    

请记住 Bassam Alugili's answer 引用自 EF Core 文档,Database.EnsureCreated()Database.Migrate() 不能一起使用,因为它们可以确保将现有迁移应用到数据库,该数据库是在需要时创建的。另一个只是确保数据库存在,如果不存在,则创建一个反映您的 DbContext 的数据库,包括通过上下文中的 Fluent API 完成的任何播种。

【讨论】:

我发现在内存数据库中播种的最优雅的方式,谢谢 这是种内存数据库的一种非常聪明的方法。谢谢。【参考方案5】:

此外,如果您在上下文的构造函数中调用它,您可能会看到性能下降...在将 EnsureCreated 移动到 setup.cs 实用程序后,我注意到响应时间有了相当大的改进。

注意:我使用的是 EFC 和 UWP。

【讨论】:

【参考方案6】:
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<YourDbContext>(option => option.UseSqlServer(@"Data source=(localdb)\ProjectModels;Initial Catalog=YourDb;Integrated Security=True"));

var app = builder.Build();

// Configure the HTTP request pipeline.
YourDbContext dbcontext = app.Services.GetRequiredService<YourDbContext>();
dbcontext.Database.EnsureCreated();

【讨论】:

以上是关于如何以及在何处调用 Database.EnsureCreated 和 Database.Migrate?的主要内容,如果未能解决你的问题,请参考以下文章

JIT 编译后如何(以及在​​何处)加载本机代码?

__eq__ 在 Python 中是如何处理的以及按啥顺序处理?

htmlspecialchars() 或 如何以及在何处使用?

在微服务架构中如何以及在何处解析外键

如何以及在何处使用 Transformations.switchMap?

在反应中如何以及在何处获取数据[重复]