EntityFrameWork数据迁移——暨改变数据库结构的方法

来源:互联网 发布:php官方文档 编辑:程序博客网 时间:2024/06/03 22:44

EntityFramework在对数据迁移时的支持并不好,现在到了6.3版本以后,Model-First的方法也被抛弃,而Code-First的方法更多的是针对首次建立数据模型的情况。一旦想要修改现有的数据库表结构,对数据库进行迁移(升级)就会遇到各种问题。

在使用EF进行数据迁移时,如果我们修改的不是主键的话,那还是比较简单,增,删减字段,都是可以的,但仍旧需要打开EF的自动合并。

具体方法如下:
(一)在vs的程序包管理控制台中,在默认项目中,选中已经引用EF框架的工程,然后输入enable-migrations –EnableAutomaticMigration:$true
(二)在该项目下会自动生成一个Migration目录,其中会有一个Configuration.cs文件。在这个文件中,默认生成的构造函数中会有两行代码

 AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true;

第一行表示是否允许自动更新,第二行表示在自动更新时是否允许数据的丢失(比如删减了表字段)
(三)接下来其实什么都不用做了,只要你不改主键,其他字段随便怎么改都可以。当然部分改变,比如数据类型的转变,可能需要做一定的逻辑处理,这个可以通过继承自DbContext类,并重载其中的OnModelCreating函数,在函数中使用modelBuilder来实现,还有Conversion类等,这里不详细说明,可以查阅相关资料

如何更新主键

上述方法在遇到需要更改主键时(比如删除原来的主键,新建另一主键),或者更改主键的相关属性时,比如主键是否是自增长ID,也就是使用了EF中对Key的DataAnnotations特性时,当你执行程序,并希望EF完成自动更新时,就会收到一个异常,这个异常有可能时一个NullReferenceException,也有可能是EF通知你有什么Table doesn’t exist。如果去查看抛出异常的InnerException,基本上都是告诉你xxx table doesn’t exist。或是One Table must only one Auto crement Column之类的。

这是因为EF在执行对主键的自动更新时,似乎是会删除原有的表,再新建,并copy原有的数据。EF极有可能是删除原有主键,再新建主键(即时是面对同一字段)。而在这个过程中,关系型数据库的操作因为没有主键,而导致操作失败。

简单的说,EF内部怎么完成这事我们不关心(其实是我没空去看它的源代码)。总之,在执行这个主键更新操作的时候,EF并不是用了一种很聪明,或是很妥善的办法,并不是像我们执行sql语句一样,简单的把主键删除,另外指定,或是更改了其属性。因为EF内部存在一些缓存机制,当EF尝试主键更新时,它用的是最粗暴的推倒重来的方法,那么一旦表失去了主键,当然接下来的操作都失败了

解决的办法如下
(一)在执行过前面的enable指令的前提下,首先关闭AutomaticMigrationsEnabled,将其设为false
(二)在默认项目选定为使用EF的项目下,执行add-migration “xxx”,注意看这时的输出,如果提示你要再次执行,以获得完整的migration,就按提示执行。成功后,会看到migration目录下多出一个文件。xxx就是这个自动产生的文件名的前缀,EF会在前缀后自动添加时间,因此这个前缀可以保持不变,这样以后再有更新,变化的只是前缀后的时间而已
(三)如果你是更新主键,这时可以看到这个文件中生成了两个函数,Up是更新函数,Down是回滚函数。在Up函数中,你可以看到第一行就是DropPrimaryKey,撤销了主键,这就验证了我们前面的猜想。接下来是AlterColumn,修改字段,然后第三句是AddPrimaryKey。照理这三句是EF生成的,执行起来应该没问题吧,但EF就是干了这么傻的事
(四)此时如果你执行Update-Database进行手动更新数据库,那么在第一句DropPrimaryKey执行完后,就会抛出前面提到的异常。因为,将第一,第三句都删除,只保留AlterColumn就可以了

需要注意的是,当使用EF进行主键更新时,不能使用自动更新合并的方式,因为如果AutomaticMigrationsEnabled = true,EF还是会根据你的entity实例的结构尝试更新数据库,此时虽然也是会执行我们通过add-migration生成的更新代码,但在该代码执行前,EF就已经使用entity来更新了,必然报错。所以一定要让AutomaticMigrationsEnabled = false

另外要注意,每次执行这个生成的更新文件,数据库的_migration表里(打开自动更新后,EF自动创建的表)会以该文件名来生成一条记录,因此这个文件你要保存下来。作为下次执行add-migration时生成增量更新代码的依据。如果你删除了这个文件,那么下次的更新文件中会包含已经修改过的内容,这样操作到DB上是会引起问题的。另外就是_migratio表中的记录千万不能删除,如果表中找不到对应的更新文件的记录,EF就是再次执行更新文件对数据库进行更新,显然这也是会有问题的。

总之,使用EF对数据库进行更新的支持并不是很友好,慎用