我可以在ASP Core Web API中使用EF Core确保数据库已迁移到最新的迁移吗?我知道可以通过命令行完成此操作,但我想以编程方式实现。
我可以在ASP Core Web API中使用EF Core确保数据库已迁移到最新的迁移吗?我知道可以通过命令行完成此操作,但我想以编程方式实现。
db.Database.EnsureCreated()
的文档中有一条注释:
请注意,此 API 不使用迁移来创建数据库。此外,使用此方法创建的数据库无法在以后使用迁移进行更新。如果你要使用迁移针对关系型数据库进行操作,可以使用
DbContext.Database.Migrate()
方法来确保已创建数据库并应用所有迁移。
你可能只需要调用 db.Database.Migrate()
。
上面的注释摘自声明的源代码,点击此处可以找到源代码。
您可以使用
db.Database.EnsureCreated();
如果您希望将数据库更新到当前模型,请使用以下命令。如果您想启用迁移(如果存在后续迁移),则使用
要使数据库与当前模型同步,请运行以上命令。如果需要启用迁移(若有后续迁移),请使用:
db.Database.Migrate();
并在随后的迁移中逐步实现。
using Microsoft.EntityFrameworkCore;
的引用。 - MichaelDatabase.Migrate
的正确位置是什么? - Shimmy WeitzhandlerConfigure
方法中。并且看起来像这样context.Database.Migrate();
你需要将 DbContext
注入到 Configure
方法中。 - Newteq Developer使用以下代码来运行迁移:
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();
}
}
Migrate
不再起作用。网络应用程序无法正常启动且没有错误消息。我将项目标记为<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
,然后它又能正常工作了。 - Dejan根据@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>();
在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找到。
Configure()
方法中添加多个上下文参数并不是很方便。我遵循了IStartupFilter
的方法,在应用程序启动时自动完成迁移。 - SvenConfigure()
方法中只需要一个上下文参数 - https://github.com/cornflourblue/aspnet-core-3-registration-login-api/blob/master/Startup.cs#L90它根据环境映射到不同的具体类型 - https://github.com/cornflourblue/aspnet-core-3-registration-login-api/blob/master/Startup.cs#L33-L36 - Jason Watmore我采用了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);
};
}
}
第一个上下文
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()
方法。
.SetCommandTimeout(160);
这是暂时的还是会持久存在? - sommmen使用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>();
});
}
using (var Scope = app.services.CreateScope())
{
var context = Scope.Services.GetRequireService<DBContext>();
context.Database.Migrate();
}
using (var scope = app.ApplicationServices.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<T>();
dbContext.Database.Migrate();
}
根据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();