3

我目前正在开发具有版本 1 用户现有代码库的应用程序的版本 2,但我还需要向新用户推出新版本(即全新安装)。版本 1 使用实体框架 4.1。版本 2 使用实体框架 5。

我的问题是我需要能够将现有的版本 1 用户升级到版本 2 并将他们的数据库迁移到新版本的架构。新模式添加了许多新表,保留了几个现有表不变并删除了其他表。

我在旅行中发现 EF4.1 不包含_MigrationHistory表格。所以我回到了我的原始版本 1 代码,将其升级到 EF5 并运行初始迁移,如下所示:

Add-Migration Initial -IgnoreChanges

这可以很好地使原始版本 1 (EF4.1) 数据库为迁移做好准备(即添加_MigrationHistory表)。然后我将它移植到我的版本 2 代码并运行:

Update-Database

它创建了_MigrationHistory表,然后我运行:

Add-Migration Version2

这会很好地将数据库迁移到版本 2。

如果数据库已经存在于版本 1 架构或版本 2 架构中(在后一种情况下不应用迁移),这将非常有效,因为我正在使用以下初始化程序:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext, Configuration>());

但是,如果数据库不存在,则Initial迁移成功运行,但Version2迁移失败,因为某些迁移步骤会删除空数据库中显然不存在的表、索引和外键。

所以我完全不知所措——如何将现有的版本 1 用户迁移到版本 2(并保留他们的数据),而且还支持需要完全从头开始创建数据库的新安装?

我已经看到很多关于创建和运行 SQL 脚本以进行迁移的想法,但我确实需要在我的应用程序启动时自动完成。最终用户应该只需要运行一个 MSI 文件就可以升级。

更新:这是 w.brian 下面建议的解决方案(对不起,我还不能投票):

string connectionString = ConfigurationManager.ConnectionStrings["DbContext"].ConnectionString;
if (Database.Exists(connectionString))
{
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<DbContext, Migrations.Configuration>());
}
else
{
    Database.SetInitializer(new DefaultDataInitialiser());
}

UPDATE2:以上似乎是第一次通过新创建工作。但是,下一次运行通过Database.Exists()检查并尝试迁移数据库。但是,由于表中只有一个条目_MigrationHistory(由新安装/全新安装生成)并且迁移有两个条目(一个用于 EF4.1 迁移;一个用于新模式),因此迁移逻辑从头开始并尝试应用每个迁移。

这失败了,因为它试图创建数据库中已经存在的表——它创建了这些表作为全新安装的一部分!

所以关键是,这种方法搞乱了迁移逻辑。

UPDATE3:所以我想我已经弄清楚如何创建初始数据库并允许后续运行仅应用迁移。从上面看,如果数据库已经存在,继续迁移代码。

如果数据库尚不存在,请DefaultDataInitialiser在方法中使用 and Seed(),执行以下操作:

context.Database.ExecuteSqlCommand(@"DELETE FROM [__MigrationHistory]");

byte[] version1Model = ConvertHexStringToByteArray("1F8B...");
context.Database.ExecuteSqlCommand(@"INSERT INTO [__MigrationHistory]
                                        ([MigrationId]
                                        ,[Model]
                                        ,[ProductVersion])
                                    VALUES
                                        ('201302082120145_Initial'
                                        ,{0}
                                        ,'5.0.0.net40')", new object[] { version1Model });

byte[] version2Model = ConvertHexStringToByteArray("1F8B...");
context.Database.ExecuteSqlCommand(@"INSERT INTO [__MigrationHistory]
                                        ([MigrationId]
                                        ,[Model]
                                        ,[ProductVersion])
                                    VALUES
                                        ('201302082200350_Version-2'
                                        ,{0}
                                        ,'5.0.0.net40')", version2Model);

从迁移逻辑的角度来看,这使新数据库“完全迁移”。下次运行应用程序时,数据库存在并且MigrateDatabaseToLatestVersion魔法运行,看到数据库是最新版本(或将来升级到下一次迁移)并且很高兴。

Seed()不幸的是,将来从头开始创建数据库时,每次新的迁移都需要添加到此方法中。

我从哪里得到用于模型的十六进制字符串?我从数据库的迁移版本中从 SQL Server Management Studio 复制了它。

4

1 回答 1

1

我只会MigrateDatabaseToLatestVersion在升级时使用初始化程序,而不是在新安装时使用。这将要求您直接查询 SQL Server 以查看数据库是否存在,但我认为这应该不会太难,因为您可以访问连接字符串。

于 2013-02-09T22:09:58.923 回答