本文讨论的内容是基于EF4.1版本。文中谈论的现有的数据库不是由EF创建。本文假定你已经对Code First迁移有一定的了解,如果不了解Code First迁移更新数据库可以查看
文章涉及的主题如下:
1、创建模型
2、可迁移性
3、添加一个初始迁移
a、使用现有的schema作为起点
b、以一个空数据库作为起点
4、注意点:
a、默认的/计算的名称可能与现有schema不匹配
b、不是所有的数据库对象都在model中表现出来
一、创建模型
第一步是创建一个以现有数据库为目标的Code First model。
注意:在这个主题中,对模型做任何修改之前按照其余的步骤操作是很重要的,您的模型需要修改数据库模式。下面的步骤需要同步模型与数据库模式。
二、可迁移性
接下来一步是使数据迁移。你可以在NuGet程序包管理器控制台中运行Enable-Migrations命令来实现。
这个命令将会在你的解决方案中创建名为Migrations的文件夹并在文件夹中创建一个名为Configuration的类。Configuration类是用来为应用程序配置数据迁移的。
三、添加一个初始迁移
一旦创建了数据库迁移和应用到本地数据库您可能还想将这些更改应用于其他数据库。例如,您的本地数据库可能是一个测试数据库,你也可能最终要应用这些更改到生产数据库或其他开发人员测试数据库。这一步有两个选择,你应该选择取决于其他数据库的模式目前是否是空的或与本地数据库的模式是否匹配。
方式一:使用现有的schema作为起点(或作为开始)
当其他数据库迁移将被应用到有相同的模式的本地数据库时,您应该使用这种方法。例如,如果你目前本地测试数据库匹配v1的生产数据库之后,你将会应用这些迁移来更新生产数据库到v2。
方式二:以一个空数据库作为起点(或者作为开始)
当数据库迁移应用到空数据库(或者不存在的数据库)时,你应该选择这种方法。例如,如果你使用一个测试数据库来开发你的应用程序,完成之后没有使用数据库迁移而是从头开始创建一个生产数据库。
四、两种方式的具体操作
方式一:
Code First数据库迁移通过模型的快照存储对模型所做的最新变更。因为我们假设数据库已经是当前模型的模式,我们将生成一个空的(操作)以当前的模型作为一个快照的迁移。
1、在包管理器控制台中运行Add-Migration InitialCreate -IgnoreChanges命令。这条命令会创建一个以当前模型作为快照的空的迁移。
2、在包管理器控制台中运行Update-Database命令。这条命令将会把创建的初始迁移应用到数据库。如果实际的迁移没有包含任何的改变,那么会简单的添加一条记录到__MigrationsHistory 表以表明迁移已经被应用。
方式二:
在这个方式中,我们需要使用迁移来从头开始创建整个数据库——包括已经在本地数据库存在的表。我们将会生成一个包含这种逻辑的初始迁移并以现有的schema来创建。然后,会使迁移应用到我们现有的数据库中。
1、在包管理器控制台中运行Add-Migration InitialCreate命令。这条命令会在现有的schema中创建迁移。
2、注释掉新创建的迁移中Up方法的所有代码。这样做可以让我们应用产生的迁移到本地数据库并且EF不会去创建已经存在的所有表。
3、在包管理器控制台中运行Update-Database命令。这会在数据库中应用InitialCreate迁移。因为实际上迁移并不包含任何更改,那么会简单的添加一条记录到__MigrationsHistory 表以表明迁移已经被应用。
4、取消Up方法中注释掉的代码。这就意味着当这个迁移被应用到以后的数据库中时在本地数据库中已经存在的schema就会通过迁移被应用。
五、注意事项
1、默认/预测的列或表的名称与现有的数据库的匹配
迁移为将要迁移创建的表和列都明确地指定了名称。然而,当使用这个迁移的时候会对数据库中其他的对象应用这些指定的默认的名称。迁移中还包括索引和外键约束。当针对现有的schema时,这些默认的名称与实际存在的数据库可能不匹配。
注意以下几点:
a、如果选择方式一
如果将来你的model发生了改变就需要改变或者删除其中与其他命名不同的那一个数据库对象,同时你需要修改脚手架迁移程序来指定正确的表或列名称。Migrations APIs中有重载的方法,可以通过修改可选的参数来实现修改名称。例如,你的现有的数据库可能有一个Post表,表中包含一个BlogId外键列,列名为IndexFk_BlogId。然而,如果使用迁移中默认的名称会被重新命名为IX_BlogId。如果你修改了model将会导致删除这个索引,你需要修改脚手架DropIndex调用来指定索引名为IndexFk_BlogId。
b、如果选择方式二
(1)针对你的本地数据库尝试执行初始迁移中的Down方法可能会失败,因为迁移程序将会尝试删除名字正确的索引和外键。这只会影响你的本地数据库或表而其他的数据库或表将会通过初始迁移中的Up方法来从头创建。
如果你想降级你现有的数据库到空的状态,通过手工实现是最简单的方式,你可以手动删除数据库或者所有的数据库表。然后,所有的数据库对象都会被重新创建并被命名为默认的名称,这样这个问题就不会在出现。
(2)如果将来你的model发生了改变就需要改变或者删除其中与其他命名不同的那一个数据库对象,针对你本地数据库的程序将不能正常工作,因为数据库对象名与默认的名称不匹配。然而,针对从头开始创建的数据库的程序是可以工作的,因为数据库对象使用的名称是迁移中默认的名称。
你也可以手动在本地数据库中做这些修改,或者考虑使用迁移从头来创建你的数据库。
(3)使用初始迁移中的Up方法创建的数据库可能与你本地数据库有明显的不同,因为以索引和外键约束为默认名称的名称会被使用。你也可以得到额外的索引作为迁移将以默认外键列来创建索引——创建出来的数据库可能不是你原来的本地数据库。
2、不是所有的数据库对象都在model中变现出来
没有在model中表示出来的数据库对象不会被Migrations处理。这些没有在model中表示出来的数据库对象包括视图、存储过程、权限许可、表、索引等等。
注意以下几点:
a、不管你选的是方式一还是方式二,如果以后你修改了model需要修改或删除这些额外的对象,Migrations不会知道发生了什么样的修改。例如,你删除了额外对象中的一列,Migrations将不会知道你删除的是什么。如果想让Migrations知道发生的修改,你需要手动将删除的列添加到脚手架Migration中。
b、如果你选择方式二,这些额外对象不会被初始的migration的Up方法创建。
如果你希望Up和Down方法监听这些额外的对象,你可以对Up和Down方法进行修改。对于对象,在Migrations API中不是一开始就被支持的,例如视图,你可以使用DbMigration.Sql方法执行SQL来创建或删除这些对象。