使用EF Core更新与修改生产数据库

使用EF Core的Code First,在设计阶段,直接使用Database.EnsureCreated()EnsureDeleted()可以快速删除、更新最新的数据结构 。由于没有什么数据,删除的风险非常低 。但是对于已经投入生产的数据库,这个方法就绝对不可行了 。
考虑以下场景:项目已经上线,一直使用本地测试数据库进行开发,本地已经增加和修改了较多数据库表结构 , 线上数据庞大且实时更新,现在测试完毕需要进行上线 。
如果需要更新生产数据库 , 我能想的有两种方法:
从一开始就使用Migration从数据库开始设计的时候,就使用EF Migration , 保证数据库能够与代码同步,不过操作的时候,需要极为小心,务必要检查生成的更新数据库代码,直接连接生产数据库,
需要注意的事项:

  • 从一开始就使用Migration,任何时候都不要使用Context.Database.EnsureCreated或者EnsureDeleted语句 。
  • 使用Add-migration之后,不要删除生成的Migration文件,这些文件记录了数据结构的变化历史 。
  • 并不是所有的变化都能自动识别,比如“修改表列名称大小写”,这种情况很多时候生成的数据是执行删除然后再新建,和我们重命名的初衷相去甚远 。因此要特别检查migrationBuilder.Drop相关的页面 。
使用Scaffold如果一开始就没有使用migration进行同步的话,那么使用EF Core将无法直接更新,我们需要变通一下:
逆向数据库到模型首先需要数据库的数据结构逆向到模型,我们使用Scaffold就可以了,详细文档就可以查看这里,需要注意的是,我们的场景下,已经有修改好的DataContext与Model,在进行scaffold的过程中,一定要指定outputdir和context,不要和当前的文件冲突 。
根据自己的喜好,选择是否采用-DataAnnotations , 另外也可以使用-table指定需要修改的表,没有被指定的表,将保持原样 。默认EF Core会按照自己的命名规则重新命名,如果你想保留自己的套路,那么使用-UseDatabaseNames参数 。
Add-Migration输出的模型我指定放在Models文件夹,原来的Models文件夹,我改成了Models1,并且更换了命名空间以保证项目现在能够正常编译 。
  • 导出的模型与DbConext:Models.Models命名空间,Models文件夹
  • 新模型与DbConext:Models命名空间,Models1文件夹接下来运行Add-Migration
add-migration initialcreate -context exportedContext这样会在Migrations文件夹下面生成一个snapshot和一个migration文件 。snapshot是当前数据库的跟踪,另外一个是运用update-database时系统会执行的操作 。里面有一个Up()和一个Down()方法,Up是执行更新时EF对数据库的操作 , Down是回滚当前更改 。由于这是第一次执行add-migration,EF Core会认为数据库现在还是空的 , 因此两个方法都有大量的语句,我们删除所有create和drop相关的语句,我这边是全部删除了 , 只留下空方法 。
应用迁移 , 同步前面准备工作已经到位了,这一步将直接操作数据库了 。使用update-database将当前的migration更新到数据库,由于我们现在的数据结构和生产数据库的数据结构一模一样,实际上我们不需要执行什么操作(删除了Up、Down内部的代码),执行Update-Database只是让EF Core将Models和生产数据库建立联系 。
我理解只是添加__EFMigrationsHistory中的记录,以便EF Core后续追踪 。
修改模型内容将Models1中的文件覆盖Models中的文件,由于类型命名的差异 , 可能会提示一些错误,按照自己的习惯修改就好了 。接下来是循序渐进,一点点修改模型 , 并经常add-migration,观察生成的语句是否正常 。
由于我使用了Identity,在数据中有对应的AspNet开头的表 , 这些表我并不在本系统中使用(其他系统需要用),因此我删除了对应的模型、snapshot、DbContext记录,运行Add-Migration,生成了如下文件:
protected override void Up(MigrationBuilder migrationBuilder){migrationBuilder.DropTable(name: "AspNetRoleClaim");migrationBuilder.DropTable(name: "AspNetUserClaim");migrationBuilder.DropTable(name: "AspNetUserLogin");migrationBuilder.DropTable(name: "AspNetUserRoles");migrationBuilder.DropTable(name: "AspNetUserToken");migrationBuilder.DropTable(name: "AspNetRole");migrationBuilder.DropTable(name: "AspNetUser");}说明现在已经能够正常跟踪我们的修改了,不过我这里需要保留对应的表,因此删除up与down的所有内容 。

推荐阅读