ASP - 在启动时核心迁移 EF Core SQL DB
Posted
技术标签:
【中文标题】ASP - 在启动时核心迁移 EF Core SQL DB【英文标题】:ASP - Core Migrate EF Core SQL DB on Startup 【发布时间】:2016-10-13 07:18:21 【问题描述】:是否可以让我的 ASP Core Web API 确保使用 EF Core 将数据库迁移到最新的迁移?我知道这可以通过命令行完成,但我想以编程方式完成。
【问题讨论】:
还没有完成,应该下个版本实现。您可以使用 Khan 下面发布的解决方法。 查看下面的答案。您应该使用 EnsureCreated 或 Migrate。两者都不是。 官方文档说:'不要在 Migrate() 之前调用 EnsureCreated()。 EnsureCreated() 绕过迁移来创建架构并导致 Migrate() 失败。 :docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/… 【参考方案1】:关于调用db.Database.EnsureCreated()
的文档中的注释:
请注意,此 API 不使用迁移来创建数据库。在 此外,创建的数据库以后不能使用 迁移。如果您的目标是关系数据库并使用 迁移,您可以使用 DbContext.Database.Migrate() 方法 确保创建数据库并应用所有迁移。
您可能只想致电db.Database.Migrate()
。
评论取自上述声明here的来源。
【讨论】:
【参考方案2】:你可以使用
db.Database.EnsureCreated();
让您的数据库与您当前的模型保持同步。如果要启用迁移(如果怀疑后续迁移),请使用
db.Database.Migrate();
并随着时间的推移进行后续迁移。
【讨论】:
EnsureCreated() 工作正常但是“context.Database.Migrate()”没有 Migrate() 方法为什么会这样? EF 核心 @Floxy 我想你错过了using Microsoft.EntityFrameworkCore;
。
@Michael 是的!我忘了说谢谢!
在 ASP.NET Core 应用程序中调用Database.Migrate
的正确位置在哪里?
@Shimmy 它应该放在Configure
方法中。看起来像这样context.Database.Migrate();
,您可以将DbContext
注入Configure
方法【参考方案3】:
使用下面的代码在
处运行迁移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();
【讨论】:
从 .NET Core 2.1 升级到 3.0(隐式地从进程外托管到进程内托管)后,从启动调用Migrate
对我来说停止工作。 Web 应用程序不会在没有错误消息的情况下启动。我已将该项目标记为<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
,然后它再次工作。【参考方案4】:
这在 ASP.NET Core 3.1 中适用于我,只需在 ConfigureServices
方法中注册现有的 Configure
方法后将其作为参数注入 db 上下文。
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
【讨论】:
如果您只有一个或几个上下文,则此方法有效。但如果你有很多上下文,应该有另一种方式 它也适用于多种上下文。我在具有从单个基本上下文继承的多个上下文的项目中使用它 - jasonwatmore.com/post/2019/10/14/… 我的意思是在Configure()
方法中添加多个上下文参数不是很方便。我按照IStartupFilter
方法在应用启动时自动完成迁移。
Configure()
方法中只需要一个上下文参数 - github.com/cornflourblue/aspnet-core-3-registration-login-api/… 它根据环境映射到不同的具体类型 - github.com/cornflourblue/aspnet-core-3-registration-login-api/…
好的,我明白了。你的用例是不同的。您只有 1 个上下文,但有两种不同的形式。我认为这是一种特殊的行为。一般来说,人们希望在应用启动时自动迁移应用内的所有数据上下文。例如,我们目前有 9 个数据上下文连接到 4 个不同的数据库(都是 SQL 服务器)。【参考方案5】:
根据@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>();
【讨论】:
【参考方案6】:根据 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();
【讨论】:
【参考方案7】:此代码适用于 .NET core 3.0
using (var scope = app.ApplicationServices.CreateScope())
var dbContext = scope.ServiceProvider.GetService<T>();
dbContext.Database.Migrate();
【讨论】:
【参考方案8】:我遵循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()
方法。
【讨论】:
【参考方案9】:使用 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>();
);
【讨论】:
【参考方案10】:我这样做是为了使用 EF Core 2.1.2 和 SQL Server 以编程方式迁移,基于此处先前的答案和“How and where to call Database.EnsureCreated and Database.Migrate?”上的bailando bailando's answer:
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。
【讨论】:
【参考方案11】:这是对先前创建扩展方法的答案的轻微修正。它修复了编写方式引发的错误。
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();
【讨论】:
【参考方案12】:在 Asp core 6 中,您没有 StartUp ,在以前版本的 asp 中,我们有 Configure 方法,允许直接访问 ServiceProvider,然后我们可以使用 GetServices 获取 DBcontext,然后调用 Migrate 方法。
但现在在 Asp core 6 中。我们应该创建一个范围,然后获取 DBcontext 对象
using (var Scope = app.services.CreateScope())
var context = Scope.Services.GetRequireService<DBContext>();
context.Database.Migrate();
【讨论】:
以上是关于ASP - 在启动时核心迁移 EF Core SQL DB的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 EF Core 在 ASP.NET Core 中取消应用迁移
用于 ASP.NET Core 应用程序的 EF Code First 迁移替代方案