EntityFramework Core 自动迁移 是否需要调用 Migrate()?检测是否需要迁移澄清迁移 vs EnsureCreated

问题描述

在我的项目中,我有 2 个数据库一个是我的自定义数据库,另一个ApplicationDbContext 给我的 Microsoft Identity

在我的 Startup.cs 中,我有以下代码

public void Configure(IApplicationBuilder app,IWebHostEnvironment env,ApplicationDbContext db,MyContext dbPSC)
{
     // ...

    db.Database.EnsureCreated();
    db.Database.Migrate();

    dbPSC.Database.EnsureCreated();
    dbPSC.Database.Migrate();
}

不知道什么时候有迁移迁移,对吧?然后,我有 2 个错误

  • 除了第一次之外,每次应用程序启动时 ApplicationDbContext 的迁移都会引发错误
  • 我的上下文的迁移看起来不错

最好的做法是什么?是否需要调用 ApplicationDbContext 的迁移?

更新

我已删除 Migration 文件夹。然后,将 Startup.cs 更改为

public void Configure(IApplicationBuilder app,MyContext dbPSC)
{
     // ...

    db.Database.EnsureCreated();
    dbPSC.Database.EnsureCreated();
}

但是当应用程序启动时,它根本不创建任何表。 AuditDbContext 是因为我使用 Audit.net

public class MyContext : AuditDbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options) { }

    public DbSet<Message> Messages { get; set; }
    public DbSet<AuditMessage> Audit_Messages { get; set; }

    #region Common Tables
    public DbSet<Country> Countries { get; set; }
    public DbSet<AuditCountry> Audit_Countries { get; set; }
    #endregion
    #region Seed
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Seed();
    }
    #endregion
}

更新 #2

我尝试了另一种解决方案,但它不起作用。解决方法是像下面的代码一样使用RelationalDatabaseCreator

public void Configure(IApplicationBuilder app,MyContext dbPSC)
{
     // ...

    db.Database.EnsureCreated();
    dbPSC.Database.EnsureCreated();
    RelationalDatabaseCreator databaseCreator = 
     (RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
    databaseCreator.CreateTables();
}

作为 Migrate(),应用程序第一次运行时会创建表,但第二次会引发错误,因为这些表已存在于数据库中。

解决方法

问题很可能是您调用了 EnsureCreated()

来自docs

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

所以,调用 Migrate() 就足够了。但我不怪你,因为 EnsureCreated() 绝对是一个误导性的方法名称。它做了很多事情,因为它还创建了甚至不基于迁移,而是基于实体的当前状态的数据库架构。

但是如果您不想对 Identity 上下文使用迁移(因为您不以任何方式扩展这些实体),则相反:您不需要调用Migrate(),调用 EnsureCreated() 就足以确保第一次创建架构。

是否需要调用 Migrate()

至于什么是最佳实践:一些开发人员根本不喜欢从代码中调用 Migrate(),因为他们认为像这样的 DB 模式操作应该受到更多控制(我相信你知道你可以也从 CLI 执行 db update)。我猜这取决于您的要求。

关于为身份表使用单独的数据库的这种特定情况:它们很可能永远不会改变,尤其是它们不会自己创建迁移。所以我会说调用 Migrate() 是不必要的,除了它可以创建和迁移不存在的数据库的事实(因此保留这个原因可能很有用;如果你是开始使用该上下文的迁移)。

检测是否需要迁移

您可以使用 context.Database.GetPendingMigrationsAsync() 来检查是否需要迁移。它是如何完成的,DB 有一个 EFMigrationsHistory 表,用于存储已应用迁移的 Id,可以将其与您的解决方案中的迁移文件进行比较。

但我认为大多数开发人员不会将其称为 GetPendingMigrationsAsync()(或同步版本,就此而言),因为您可以直接调用 Migrate(),如果数据库已经更新,则无害完成了。

澄清迁移 vs EnsureCreated

我不确定您与 EF 合作了多少,所以这很明显。但是当您已经使用例如创建迁移时使用 Migrate() dotnet ef migrations add <migrationname>,这是一种使用代码优先的方法逐步改变数据库架构的方法。

但是如果您根本不使用迁移,那么您将拥有一个非常简单的架构,不需要随着迁移而增量更改,并且您只想确保数据库存在一个架构,然后使用只使用 EnsureCreated(),不要使用 Migrate()

问题是,如果您碰巧更改了实体,EnsureCreated() 将不会更新数据库;如果数据库已经存在,它什么都不做。因此,您必须先调用 EnsureDeleted(),然后调用 EnsureCreated() 才能实现无需迁移的最新数据库架构。这显然涉及丢失所有数据。这就是迁移有用的原因。 EnsureCreated() 主要用于例如集成测试。


顺便说一句,您可以为自己的表和身份使用相同的数据库上下文;我敢说这是与 EF 合作的“正常”方式。当然,我想你可能有你分开的具体原因。

,

您不需要同时使用 db.Database.EnsureCreated();db.Database.Migrate(); 当您的应用程序中没有启用迁移时,您需要使用 db.Database.EnsureCreated();。如果您启用了迁移,那么只使用 db.Database.Migrate(); 就足够了。

但是如果您不启用迁移,那么在每次更改时您都必须重新创建您的数据库。因为“EnsureCreated”只验证数据库是否已经存在。所以最好启用迁移。

因此,如果您不想更改 ApplicationDbContext 中的任何内容以便可以使用 db.Database.EnsureCreated(); 但如果您想更改某些内容或者您可能想在 AspNetUsers 等中添加更多字段,那么您应该启用迁移并使用db.Database.Migrate();

顺便说一句,如果需要,您也可以在 ApplicationDbContext 中添加自定义表。

,

根据你们发给我的回复,我的解决方案是使用迁移。在 Startup.cs 中,我添加了这些代码行(dbPSC 是 DbContext

dbPSC.Database.EnsureCreated();
if (dbPSC.Database.GetPendingMigrations().Count() > 0)
    dbPSC.Database.Migrate();