写点什么

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

作者:高端章鱼哥
  • 2023-08-25
    福建
  • 本文字数:3279 字

    阅读完需:约 11 分钟

使用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 的所有内容。


注意以下几点:

1.更新模型名称

如果使用 fluentAPI,那么模型对应的表名称会直接在 fluentAPI 中直接指定,只修改模型的名称没有任何效果。修改的话,可以修改对应的 fluentAPI,或者换用 Annotation

2.提示找不到 constraint

对于修改主键、索引等内容的情况,如果不是通过 EF Core 建立的数据库,那么命名规则可能不一样。对于 postgresql 数据库,可以用这个查询名称,然后修改对应的 migration 文件内容即可。

SELECT * FROM pg_CONSTRAINT
复制代码
3.复合主键的限制

对于使用两列或者以上列作为复合主键的情况,使用 EnsureCreated 方法是可以识别 Annotation 形式的主键的。

[Key][Column(Order = 1)]public string DeviceId { get; set; }[Key][Column(Order = 2)]public long Timestamp { get; set; }
复制代码


使用 Migration 的时候,这种形式无法识别,需要在 OnModelCreating()中,使用 fluentAPI:

modelBuilder.Entity<DeviceData>().HasKey(w => new { w.DeviceId, w.Timestamp });
复制代码
4.Command 执行超时

默认 Command 执行的超时设置只有 30s,对一些大一点的表来说,是不太够的。可以设置:

optionsBuilder.UseNpgsql("Server=xxxxxxxxxxxxx", opt=>opt.CommandTimeout(3000));
复制代码


增加命令执行的超时时间。

5.多个连接字符串的情况

如果程序使用了 appsettings.Development.json 之类的文件存储连接字符串,那么需要指定环境是 Production(生产数据库),否则可能还原到本地数据库去了。对于 nuget 包管理控制台(使用 update-database),执行:

$Env:ASPNETCORE_ENVIRONMENT = "Development"Update-Database
复制代码


对于使用 dotnet ef 工具集的,直接执行:

dotnet ef database update --environment Development
复制代码
6.cannot be cast automatically to type

设计数据库表如果修改列的数据类型(比如从 varchar 到 integer),Postgresql 会提示这个问题,导致无法修改。可以在 migrationbuilder 中使用 sql,按照提示添加"USING "x"::integer"解决。但是这种方法还是不太优雅,手动处理 Up()之后,还需要处理 Down(),否则将无法正确还原。


可以使用分步的方法进行,假设我们需要将 Id 从 varchar 改成 int4。

  1. 添加一个字段 temp,类型为 int4,设置为[Key],然后删除 Id 字段。

  2. 添加并应用迁移

  3. 修改 temp 名称为 Id

  4. 添加并应用迁移

多次应用迁移

每次修改尽量少一点,然后 update-database,这样更容易发现问题,对于有



这种提示的,一定要检查生成语句中 Drop 相关的语句。


本地数据库与生产数据库,都有__EFMigrationsHistory 记录相关的迁移情况。在生产与本地数据库中进行切换时,不用担心顺序问题,Update-Database 会一个个应用迁移直到最新。

总结

使用 Migration 能够降低数据库同步中很多工作量,合理利用,可以对生产用的数据库进行热更新。

注:本文在.NET 6,EF Core 6 下测试通过。


相关内容拓展:(技术前沿)


近 10 年间,甚至连传统企业都开始大面积数字化时,我们发现开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。


针对这类问题,低代码把某些重复出现的场景、流程,具象化成一个个组件、api、数据库接口,避免了重复造轮子。极大的提高了程序员的生产效率。


推荐一款程序员都应该知道的软件 JNPF 快速开发平台。这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。


体验官网:https://www.jnpfsoft.com?infoq

还没有了解低代码这项技术可以赶紧体验学习!

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
使用EF Core更新与修改生产数据库_EF Core_高端章鱼哥_InfoQ写作社区