使用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的所有内容。
注意以下几点:
更新模型名称
如果使用fluentAPI,那么模型对应的表名称会直接在fluentAPI中直接指定,只修改模型的名称没有任何效果。修改的话,可以修改对应的fluentAPI,或者换用Annotation
提示找不到constraint
对于修改主键、索引等内容的情况,如果不是通过EF Core建立的数据库,那么命名规则可能不一样。对于postgresql数据库,可以用这个查询名称,然后修改对应的migration文件内容即可。
SELECT * FROM pg_CONSTRAINT
复合主键的限制
对于使用两列或者以上列作为复合主键的情况,使用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 });
Command执行超时
默认Command执行的超时设置只有30s,对一些大一点的表来说,是不太够的。可以设置:
optionsBuilder.UseNpgsql("Server=xxxxxxxxxxxxx", opt=>opt.CommandTimeout(3000));
增加命令执行的超时时间。
多个连接字符串的情况
如果程序使用了appsettings.Development.json之类的文件存储连接字符串,那么需要指定环境是Production(生产数据库),否则可能还原到本地数据库去了。
对于nuget包管理控制台(使用update-database),执行:
$Env:ASPNETCORE_ENVIRONMENT = "Development"
Update-Database
对于使用dotnet ef工具集的,直接执行:
dotnet ef database update --environment Development
cannot be cast automatically to type
设计数据库表如果修改列的数据类型(比如从varchar到integer),Postgresql会提示这个问题,导致无法修改。可以在migrationbuilder中使用sql,按照提示添加"USING "x"::integer"解决。但是这种方法还是不太优雅,手动处理Up()之后,还需要处理Down(),否则将无法正确还原。
可以使用分步的方法进行,假设我们需要将Id从varchar改成int4。
- 添加一个字段temp,类型为int4,设置为[Key],然后删除Id字段。
- 添加并应用迁移
- 修改temp名称为Id
- 添加并应用迁移
多次应用迁移
每次修改尽量少一点,然后update-database,这样更容易发现问题,对于有

这种提示的,一定要检查生成语句中Drop相关的语句。
本地数据库与生产数据库,都有__EFMigrationsHistory记录相关的迁移情况。在生产与本地数据库中进行切换是,不用担心顺序问题,Update-Database会一个个应用迁移直到最新。
总结
使用Migration能够降低数据库同步中很多工作量,合理利用,可以对生产用的数据库进行热更新。
注:本文在.NET 6,EF Core 6下测试通过。
使用EF Core更新与修改生产数据库的更多相关文章
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
2.4.6 EF Core -- 更新 状态 自动变更检测 不查询删除和更新 并发 状态 Entity State Property State Entity State Added 添加 Uncha ...
- EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况
使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...
- EF Core利用Scaffold从根据数据库生成代码
在EF6 之前的时代,如果需要从数据库中生成代码,是可以直接在界面上操作的,而到了EF Core的时代,操作方式又有更简便的方式了,我们只需要记住以下这条指令. Scaffold-DbContext ...
- ASP.NET Core 中使用EF Core 将实体映射到数据库表的方法(SQL Server)
前段时间听过一个关于使用ASP.NET Core建立项目的视频.其中使用EF Core映射到数据库的部分是按部就班地学习.今天自己建立项目时,有些步骤已经有一些遗忘.所以写下这篇文章,顺便理清思路. ...
- EF core (code first) 通过自定义 Migration History 实现多租户使用同一数据库时更新数据库结构
前言 写这篇文章的原因,其实由于我写EF core 实现多租户的时候,遇到的问题. 具体文章的链接: Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下 ...
- [翻译 EF Core in Action 2.2] 创建应用程序的数据库上下文
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- [翻译 EF Core in Action 2.0] 查询数据库
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- Asp.Net Core 2.0 项目实战(4)ADO.NET操作数据库封装、 EF Core操作及实例
Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...
- EF Core中如何设置数据库表自己与自己的多对多关系
本文的代码基于.NET Core 3.0和EF Core 3.0 有时候在数据库设计中,一个表自己会和自己是多对多关系. 在SQL Server数据库中,现在我们有Person表,代表一个人,建表语句 ...
随机推荐
- Windows高效开发环境配置(一)
更多精彩内容,欢迎关注公众号:逻魔代码 前言 用了多年的 MacOS 做开发,一系列诸如 Alfred.Item2.Oh-my-zsh 之类的工具,大大地提升了工作的效率和使用舒适度.新工作不给配 M ...
- mybatis 01: 静态代理 + jdk动态代理
背景 有时目标对象不可直接访问,只能通过代理对象访问 图示: 示例1: 房东 ===> 目标对象 房屋中介 ===> 代理对象 你,我 ===> 客户端对象 示例2: 运营商(电信, ...
- wallpaperPKG文件提取
简单粗暴 下载这个ZIP文件链接: 下载地址戳我 提取码: ag43 解压后双击打开如下文件 我们在解压一下repkg-master.zip解压后如下,注意我的路径进入到这些很多文件的页面 返回首页复 ...
- 自定义View3-水波纹扩散(仿支付宝咻一咻)实现代码、思想
PS:自定义view篇-水波纹实现 效果:水波纹扩散 场景:雷达.按钮点击效果.搜索等 实现:先上效果图,之前记得支付宝有一个咻一咻,当时就是水波纹效果,实现起来一共两步,第一画内圆,第二画多个外圆, ...
- Oracle 与 PostgreSQL 函数行为的差异引发性能差异
对于Oracle,对于数据修改的操作通过存储过程处理,而对于函数一般不进行数据修改操作.同时,函数可以通过 Select 进行调用,而存储过程则不行. 一.对于volatile 函数的行为 1.Ora ...
- KingbaseES where 条件解析顺序
概述 KingbaseES 对于where 条件的解析严格遵守"从左到右"的原则,因此,对于选择性比较强的条件,进行最先过滤是有利于性能的. 一.KingbaseES 1.条件顺序 ...
- 自定义异常、Java网络编程
day04 throw关键字 throw用来对外主动抛出一个异常,通常下面两种情况我们主动对外抛出异常: 1:当程序遇到一个满足语法,但是不满足业务要求时,可以抛出一个异常告知调用者. 2:程序执行遇 ...
- 采云端&采云链:从订单协同到采购供应链,让采购供应链互联互通
采购供应链安全从来没有像现在这样显得如此重要和紧迫,也从来没有像现在这样复杂和敏感,对企业的经营产生决定性的影响.尤其在疫情期间,采购供应链更加牵一发而动全身,成为"运筹帷幄,决胜于千里之外 ...
- 跟羽夏学 Ghidra ——引用
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- 图与A*算法
同时根据每条边的实际情况,采用权重来表示他们的不同,权重可以是负的. 往这个图中添加顶点的成本非常昂贵,因为新的矩阵结果必须重新按照新的行/列创建,然后将已有的数据复制 到新的矩阵中. 图的数据结构: ...