ASP - Core 在启动时迁移 EF Core SQL 数据库
ASP - Core Migrate EF Core SQL DB on Startup
是否可以让我的 ASP Core Web API 确保将数据库迁移到使用 EF Core 的最新迁移?我知道这可以通过命令行完成,但我想以编程方式完成。
您可以使用
db.Database.EnsureCreated();
让您的数据库与您当前的模型保持同步。如果要启用迁移(如果怀疑后续迁移),则使用
db.Database.Migrate();
并随着时间的推移进行后续迁移。
文档中关于调用 db.Database.EnsureCreated()
的注释:
Note that this API does not use migrations to create the database. In
addition, the database that is created cannot be later updated using
migrations. If you are targeting a relational database and using
migrations, you can use the DbContext.Database.Migrate() method to
ensure the database is created and all migrations are applied.
您可能只想致电 db.Database.Migrate()
。
从声明上方找到的来源中获取的评论 here。
根据@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>();
使用下面的代码 运行 在
迁移
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();
}
}
根据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();
我这样做是为了通过 EF Core 2.1.2 和 SQL 服务器以编程方式迁移,基于此处的先前答案和 bailando bailando's answer on "How and where to call Database.EnsureCreated and Database.Migrate?":
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。
这是对先前创建扩展方法的答案的轻微修正。它修复了按照编写方式抛出的错误。
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<T>();
context.Database.Migrate();
}
}
}
}
此代码适用于 .NET core 3.0
using (var scope = app.ApplicationServices.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<T>();
dbContext.Database.Migrate();
}
这在 ASP.NET Core 3.1 中对我有用,在 ConfigureServices
方法中注册后,只需将数据库上下文作为参数注入现有 Configure
方法。
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
我遵循了 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()
方法。
使用 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>();
});
}
在 Asp core 6 中你没有 StartUp ,在以前的 asp 版本中我们有 Configure 方法允许直接访问 ServiceProvider 然后我们可以使用 GetServices 获取 DBcontext 然后调用迁移方法。
但现在 Asp 核心 6。我们应该创建一个范围然后获取 DBcontext 对象
using (var Scope = app.services.CreateScope())
{
var context = Scope.Services.GetRequireService<DBContext>();
context.Database.Migrate();
}
是否可以让我的 ASP Core Web API 确保将数据库迁移到使用 EF Core 的最新迁移?我知道这可以通过命令行完成,但我想以编程方式完成。
您可以使用
db.Database.EnsureCreated();
让您的数据库与您当前的模型保持同步。如果要启用迁移(如果怀疑后续迁移),则使用
db.Database.Migrate();
并随着时间的推移进行后续迁移。
文档中关于调用 db.Database.EnsureCreated()
的注释:
Note that this API does not use migrations to create the database. In addition, the database that is created cannot be later updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate() method to ensure the database is created and all migrations are applied.
您可能只想致电 db.Database.Migrate()
。
从声明上方找到的来源中获取的评论 here。
根据@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>();
使用下面的代码 运行 在
迁移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();
}
}
根据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();
我这样做是为了通过 EF Core 2.1.2 和 SQL 服务器以编程方式迁移,基于此处的先前答案和 bailando bailando's answer on "How and where to call Database.EnsureCreated and Database.Migrate?":
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。
这是对先前创建扩展方法的答案的轻微修正。它修复了按照编写方式抛出的错误。
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<T>();
context.Database.Migrate();
}
}
}
}
此代码适用于 .NET core 3.0
using (var scope = app.ApplicationServices.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<T>();
dbContext.Database.Migrate();
}
这在 ASP.NET Core 3.1 中对我有用,在 ConfigureServices
方法中注册后,只需将数据库上下文作为参数注入现有 Configure
方法。
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
我遵循了 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()
方法。
使用 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>();
});
}
在 Asp core 6 中你没有 StartUp ,在以前的 asp 版本中我们有 Configure 方法允许直接访问 ServiceProvider 然后我们可以使用 GetServices 获取 DBcontext 然后调用迁移方法。
但现在 Asp 核心 6。我们应该创建一个范围然后获取 DBcontext 对象
using (var Scope = app.services.CreateScope())
{
var context = Scope.Services.GetRequireService<DBContext>();
context.Database.Migrate();
}