使用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

  1. 添加一个字段temp,类型为int4,设置为[Key],然后删除Id字段。
  2. 添加并应用迁移
  3. 修改temp名称为Id
  4. 添加并应用迁移

多次应用迁移

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



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

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

总结

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

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

使用EF Core更新与修改生产数据库的更多相关文章

  1. .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记

    2.4.6 EF Core -- 更新 状态 自动变更检测 不查询删除和更新 并发 状态 Entity State Property State Entity State Added 添加 Uncha ...

  2. EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况

    使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...

  3. EF Core利用Scaffold从根据数据库生成代码

    在EF6 之前的时代,如果需要从数据库中生成代码,是可以直接在界面上操作的,而到了EF Core的时代,操作方式又有更简便的方式了,我们只需要记住以下这条指令. Scaffold-DbContext ...

  4. ASP.NET Core 中使用EF Core 将实体映射到数据库表的方法(SQL Server)

    前段时间听过一个关于使用ASP.NET Core建立项目的视频.其中使用EF Core映射到数据库的部分是按部就班地学习.今天自己建立项目时,有些步骤已经有一些遗忘.所以写下这篇文章,顺便理清思路. ...

  5. EF core (code first) 通过自定义 Migration History 实现多租户使用同一数据库时更新数据库结构

    前言 写这篇文章的原因,其实由于我写EF core 实现多租户的时候,遇到的问题. 具体文章的链接: Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下 ...

  6. [翻译 EF Core in Action 2.2] 创建应用程序的数据库上下文

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  7. [翻译 EF Core in Action 2.0] 查询数据库

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  8. 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 ...

  9. EF Core中如何设置数据库表自己与自己的多对多关系

    本文的代码基于.NET Core 3.0和EF Core 3.0 有时候在数据库设计中,一个表自己会和自己是多对多关系. 在SQL Server数据库中,现在我们有Person表,代表一个人,建表语句 ...

随机推荐

  1. Windows高效开发环境配置(一)

    更多精彩内容,欢迎关注公众号:逻魔代码 前言 用了多年的 MacOS 做开发,一系列诸如 Alfred.Item2.Oh-my-zsh 之类的工具,大大地提升了工作的效率和使用舒适度.新工作不给配 M ...

  2. mybatis 01: 静态代理 + jdk动态代理

    背景 有时目标对象不可直接访问,只能通过代理对象访问 图示: 示例1: 房东 ===> 目标对象 房屋中介 ===> 代理对象 你,我 ===> 客户端对象 示例2: 运营商(电信, ...

  3. wallpaperPKG文件提取

    简单粗暴 下载这个ZIP文件链接: 下载地址戳我 提取码: ag43 解压后双击打开如下文件 我们在解压一下repkg-master.zip解压后如下,注意我的路径进入到这些很多文件的页面 返回首页复 ...

  4. 自定义View3-水波纹扩散(仿支付宝咻一咻)实现代码、思想

    PS:自定义view篇-水波纹实现 效果:水波纹扩散 场景:雷达.按钮点击效果.搜索等 实现:先上效果图,之前记得支付宝有一个咻一咻,当时就是水波纹效果,实现起来一共两步,第一画内圆,第二画多个外圆, ...

  5. Oracle 与 PostgreSQL 函数行为的差异引发性能差异

    对于Oracle,对于数据修改的操作通过存储过程处理,而对于函数一般不进行数据修改操作.同时,函数可以通过 Select 进行调用,而存储过程则不行. 一.对于volatile 函数的行为 1.Ora ...

  6. KingbaseES where 条件解析顺序

    概述 KingbaseES 对于where 条件的解析严格遵守"从左到右"的原则,因此,对于选择性比较强的条件,进行最先过滤是有利于性能的. 一.KingbaseES 1.条件顺序 ...

  7. 自定义异常、Java网络编程

    day04 throw关键字 throw用来对外主动抛出一个异常,通常下面两种情况我们主动对外抛出异常: 1:当程序遇到一个满足语法,但是不满足业务要求时,可以抛出一个异常告知调用者. 2:程序执行遇 ...

  8. 采云端&采云链:从订单协同到采购供应链,让采购供应链互联互通

    采购供应链安全从来没有像现在这样显得如此重要和紧迫,也从来没有像现在这样复杂和敏感,对企业的经营产生决定性的影响.尤其在疫情期间,采购供应链更加牵一发而动全身,成为"运筹帷幄,决胜于千里之外 ...

  9. 跟羽夏学 Ghidra ——引用

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...

  10. 图与A*算法

    同时根据每条边的实际情况,采用权重来表示他们的不同,权重可以是负的. 往这个图中添加顶点的成本非常昂贵,因为新的矩阵结果必须重新按照新的行/列创建,然后将已有的数据复制 到新的矩阵中. 图的数据结构: ...