bai*_*ndo 59 asp.net asp.net-mvc entity-framework entity-framework-core
我有一个ASP.NET MVC 6应用程序,我需要调用Database.EnsureCreated和Database.Migrate方法.
但我应该在哪里打电话给他们?
Bas*_*ili 71
我认为这是一个重要的问题,应该得到很好的回答!
什么是Database.EnsureCreated?
context.Database.EnsureCreated()是新的EF核心方法,可确保上下文的数据库存在.如果存在,则不采取任何措施.如果它不存在,则创建数据库及其所有模式,并确保它与此上下文的模型兼容.
注意:
此方法不使用迁移来创建数据库.此外,以后无法使用迁移更新创建的数据库.如果要定位关系数据库并使用迁移,则可以使用该DbContext.Database.Migrate()方法确保创建数据库并应用所有迁移.
我们是如何使用EF 6做到的?
context.Database.EnsureCreated() 相当于下面列出的EF 6的方法:
包管理器控制台:
启用 - 迁移-EnableAutomaticMigrations.添加迁移/更新的数据库.
来自代码:
Database.SetInitializer CreateDatabaseIfNotExists
要么
使用DbMigrationsConfiguration并设置AutomaticMigrationsEnabled = true;
什么是Database.Migrate?
将上下文的任何挂起的迁移应用于数据库.如果数据库尚不存在,将创建它.
我们是如何使用EF 6做到的?
context.Database.Migrate() 相当于下面列出的EF 6的方法:
包管理器控制台:
Update-Database -TargetMigration
使用自定义DbMigrationsConfiguration:
AutomaticMigrationsEnabled = false; 或者使用DbMigrator.
结论:
如果您正在使用迁移context.Database.Migrate().如果您不想迁移并且只想要一个快速数据库(通常用于测试),那么请使用context.Database.EnsureCreated()/ EnsureDeleted().
bai*_*ndo 18
根据James P和Bassam Alugili提供的信息,我最终做的是将这行代码添加到Startup.cs-> Configure方法中.
using (var scope =
app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var context = scope.ServiceProvider.GetService<MyDbContext>())
context.Database.Migrate();
Run Code Online (Sandbox Code Playgroud)
Jam*_*s P 13
正如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();
}
Run Code Online (Sandbox Code Playgroud)
并定义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();
}
Run Code Online (Sandbox Code Playgroud)
使用context.Database.Migrate()请看这里或这里.
Kyl*_*ley 12
通常,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")));
}
}
Run Code Online (Sandbox Code Playgroud)
但是,theIServiceCollection并不充当服务提供者,并且由于DbContext未在当前作用域 ( )之前向注入容器注册,因此Startup.ConfigureServices我们无法在此处通过依赖注入访问上下文。
Henk Mollema 在此处讨论了在启动期间手动解析服务,但提到...
手动解析服务(又名服务定位器)通常被认为是一种反模式...... [并且] 你应该尽可能避免它。
Henk 还提到Startup构造函数的依赖注入非常有限,不包括在 中配置的服务Startup.ConfigureServices(),因此通过应用程序其余部分使用的注入容器使用 DbContext 是最简单和最合适的。
运行时的托管服务提供者可以将某些服务注入到
Startup类的构造函数中,例如IConfiguration、IWebHostEnvironment(IHostingEnvironment在 3.0 之前的版本中)ILoggerFactory和IServiceProvider。请注意,后者是由托管层构建的实例,仅包含用于启动应用程序的基本服务。
为了调用Database.EnsureCreated()or 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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
请记得巴萨姆Alugili的答案从EF核心文档引用了Database.EnsureCreated()和Database.Migrate()并不意味着一起使用,因为一个确保现有迁移应用到数据库,这是必要时创建的。另一个只是确保数据库存在,如果不存在,则创建一个反映您的DbContext,包括在上下文中通过 Fluent API 完成的任何种子。
小智 11
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();
Run Code Online (Sandbox Code Playgroud)
小智 6
public class AppDbContext : DbContext
{
// Properties with entities of
// public DbSet<User> Users { get; set; }
// public DbSet<Cart> Carts { get; set; }
// public DbSet<CartItem> CartItems { get; set; }
// public DbSet<Product> Products { get; set; }
// ...
public AppDbContext()
{
Database.Migrate();
// Or Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Model configuration
// modelBuilder.Entity<CartItem>().HasKey(x => new { x.CartId, x.ProductId })
base.OnModelCreating(modelBuilder);
}
}
Run Code Online (Sandbox Code Playgroud)
Database.Migrate或 的地方Database.EnsureCreated。AppDbContext。AppDbContext。AppDbContext.// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Service configurations
var app = builder.Build();
try
{
using (var serviceScope = app.Services.CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.MigrateAsync();
// or dbContext.Database.EnsureCreatedAsync();
}
// Middleware pipeline configuration
app.Run();
}
catch (Exception e)
{
app.Logger.LogCritical(e, "An exception occurred during the service startup");
}
finally
{
// Flush logs or else you lose very important exception logs.
// if you use Serilog you can do it via
// await Log.CloseAndFlushAsync();
}
Run Code Online (Sandbox Code Playgroud)
如果您有一个需要花费大量时间才能应用的大型迁移,则应用程序可能会超过可在运行该应用程序的环境中使用的启动超时。
如果发生这种情况,您可以暂时增加应用迁移的超时并将其恢复为默认值。
// InitializationService.cs
public sealed class InitializationService : IHostedService
{
#region Constructor and dependencies
private readonly IServiceProvider _serviceProvider;
private readonly Options _options;
public InitializationService(IServiceProvider serviceProvider, IOptions<Options> options)
{
_serviceProvider = serviceProvider;
_options = options.Value;
}
#endregion
public async Task StartAsync(
// Use this token to detect the application stopping
CancellationToken cancellationToken
)
{
using var serviceScope = _serviceProvider.CreateScope();
var serviceProvider = serviceScope.ServiceProvider;
if (!_options.SkipMigration)
{
var context = serviceProvider.GetRequiredService<AppDbContext>();
await context.Database.MigrateAsync(cancellationToken);
}
// ... Other initialization logic of the application. (e.g. a seeding of an initial data)
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public sealed class Options
{
public const string Position = "Initialization";
public bool SkipMigration { get; set; }
// ... Other options for initialization service
}
}
public static class InitializationServiceExtensions
{
public static void AddInitializationService(this IServiceCollection serviceCollection)
{
serviceCollection.AddHostedService<InitializationService>();
serviceCollection
.AddOptions<InitializationService.Options>()
.BindConfiguration(InitializationService.Options.Position);
}
}
Run Code Online (Sandbox Code Playgroud)
您不能在之后立即使用数据库var app = builder.Build();。
当我尝试使用 Hangfire 仪表板并在 Hangfire 和 EF Core 之间共享连接字符串时,我遇到了这个问题。Hangfire 仪表板至少需要创建一个数据库。
我通过以下代码解决了这个问题:
/// <remarks>
/// It cannot be done in <see cref="InitializationService"/>
/// because Hangfire requires that db already has been created
/// before Hangfire dashboard is configured
/// </remarks>
public static async Task CreateEmptyDbIfNotExists(WebApplication app)
{
using var serviceScope = app.Services.CreateScope();
var serviceProvider = serviceScope.ServiceProvider;
var skipMigration = serviceProvider
.GetRequiredService<IOptions<InitializationService.Options>>()
.Value.SkipMigration;
if (skipMigration)
return;
var context = serviceProvider.GetRequiredService<AppDbContext>();
var databaseCreator = context.GetService<IDatabaseCreator>() as RelationalDatabaseCreator;
if (!await databaseCreator.ExistsAsync())
await databaseCreator.CreateAsync();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
41311 次 |
| 最近记录: |