Entity Framework,自动应用迁移

20

我正在使用带有 AutomaticMigrationsEnabled = true 的 Entity Framework Code First 方法:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<DbContext, MigrateDBConfiguration>());
//////////////////////////////////

public class MigrateDBConfiguration : System.Data.Entity.Migrations.DbMigrationsConfiguration<DbContext>
{
    public MigrateDBConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
}

项目的第一次运行按预期创建了数据库和表。在添加或删除字段后,我运行了 Add-Migration。 Migration 类被生成,但是在运行项目后出现以下异常:

类型为 'System.InvalidOperationException' 的异常在 EntityFramework.dll 中发生,但未在用户代码中处理

额外信息:自数据库创建以来,与 'DBContext' 上下文支持的模型发生了更改。

编辑:根据arturo menchaca的回答建议,我修改了我的代码如下:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<DBContext, MigrateDBConfiguration<DBContext>>());

...

更改后,出现以下异常:

数据库中已经存在名为“MyTable”的对象。

我该如何应用数据库迁移?


需要检查的一件事是看看您是否更改了命名空间。实体框架迁移对命名空间非常敏感,因为这似乎是迁移中许多神秘问题发生的地方。过去我曾因调整命名空间以适应迁移默认创建的文件夹结构而受到影响。 - kmacdonald
听起来你正在将自动迁移与手动迁移结合使用。这可能会在数据库中引起一些严重的迁移历史问题(至少根据我的经验是这样)。如果你正在生成手动迁移,请尝试关闭自动迁移。 - kmacdonald
5个回答

22

自动迁移意味着您不需要为模型中的更改运行add-migration命令,但必须手动运行update-database命令。

如果您调用update-database时启用了自动迁移,并且模型中存在未决更改,则会添加一个“自动”迁移,并更新数据库。

如果您希望在无需调用update-database命令的情况下更新数据库,可以在上下文的OnModelCreating()方法中添加Database.SetInitializer(...),就像这样:

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MigrateDBConfiguration>());
    }

    ...
}

public class MigrateDBConfiguration : System.Data.Entity.Migrations.DbMigrationsConfiguration<MyContext>
{
    ...

请注意,您应该使用实际上下文声明DbMigrationsConfigurationMigrateDatabaseToLatestVersion,而不是默认的DbContext


3
这实际上是不正确的。通过使用MigrateToTheLatestMigrationVersion数据库初始化程序,这应该可以解决问题(过去我曾经成功地使用过)。我现在正面临着同样的问题。我不确定我改了什么,但我知道这个设置过去是可以工作的。 - kmacdonald
我希望我的数据库可以在不需要调用update-database的情况下进行更新,但尝试了你的解决方案却没有成功。 - Fred
@ArturoMenchaca 我的 DbContext 类名是 DbAccess,在上面的问题中写 DbContext 更简单。 - Fred
@ArturoMenchaca 在 OnModelCreating 中调用 Database.SetInitializer(...) 像最后编辑我的问题一样。 - Fred
@ArturoMenchaca 是的,它可以工作。但我想在将来应用我的显式生成的迁移,我不能每次迁移都删除MyTable。 - Fred
显示剩余4条评论

21

最终,我找到了解决我的问题的方法。我在每次应用程序启动时调用这种方法:

public void InitializeDatabase(DataAccessManager context)
{
    if (!context.Database.Exists() || !context.Database.CompatibleWithModel(false))
    {
        var configuration = new DbMigrationsConfiguration();
        var migrator = new DbMigrator(configuration);
        migrator.Configuration.TargetDatabase = new DbConnectionInfo(context.Database.Connection.ConnectionString, "System.Data.SqlClient");
        var migrations = migrator.GetPendingMigrations();
        if (migrations.Any())
        {
            var scriptor = new MigratorScriptingDecorator(migrator);
            var script = scriptor.ScriptUpdate(null, migrations.Last());

            if (!string.IsNullOrEmpty(script))
            {
                context.Database.ExecuteSqlCommand(script);
            }
        }
    }
}

4
非常感谢,这对我很有帮助!不过我稍微改进了一下,只使用migrator.Update();而不是获取脚本并直接执行它。 - David Harlow
愚蠢的问题警报:这个DataAccessManager类在哪个库中? - CodingCretin

6
如果您的实体发生了更改,您需要首先运行add-migration来创建迁移脚本。
之后在您的Global.asax中,
您需要添加以下代码:
        var configuration = new MyProject.Configuration();
        var migrator = new System.Data.Entity.Migrations.DbMigrator(configuration);            

        migrator.Update();

每次运行你的ASP.NET项目时,它会检查是否有新的迁移需要运行,并自动为你运行update-database

2
微软在运行时处理迁移,此处有相关信息。
例如,在Program.cs中可以这样做:(在.NET 5.0预览版中测试过)
public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    MigrateDatabase(host);

    host.Run();
}

private static void MigrateDatabase(IHost host)
{
    using var scope = host.Services.CreateScope();
    var services = scope.ServiceProvider;

    try
    {
        var context = services.GetRequiredService<ApplicationDbContext>();
        context.Database.Migrate();
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred creating the DB.");
    }
}

请注意,原问题与Entity Framework Core无关。请仅翻译文本内容,不要进行解释。 - Jeremy Lakeman

-1

使用 Microsoft.EntityFrameworkCore;

        await _dbContext.Database.MigrateAsync();
        _dbContext.Database.Migrate();

或者

        await _dbContext.Database.EnsureCreatedAsync();
         _dbContext.Database.EnsureCreated();

这两种方法都会检查数据库是否存在,如果不存在,则会创建它们。

  • Migrate() 使用迁移,适用于使用迁移或关系型数据库的情况。
  • EnsureCreated() 不使用迁移,这意味着一旦使用此方法创建了数据库,就不能再对其执行任何迁移。

这个答案对 Database.Migrate() 有更好的解释。它并不像你所说的那样直接起作用。 - Gert Arnold
没错,我刚刚发布了另一种方法 :) - BNG016

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接