EF Core Code First 迁移实战与常见坑
Words: 939Read Time: 3 minLast edited: 2026-8-4
为什么选 Code First
我做的项目基本是自己说了算的小系统,没有 DBA 盯着,所以 EF Core 的 Code First 用起来最顺手:实体类就是表结构的唯一事实来源,改字段就改实体,迁移命令一跑,数据库跟着走。相比 Database First 每次都要从库里同步模型,Code First 的心智负担小很多。
基本流程:从加迁移到更新库
前置工作是装好工具:
dotnet tool install --global dotnet-ef,项目里引用 Microsoft.EntityFrameworkCore.SqlServer(我用的 SQL Server)和设计时包 Microsoft.EntityFrameworkCore.Design。之后的日常就三个命令:
migrations add 会对比当前模型和上次迁移留下的快照(ModelSnapshot.cs),生成一个带 Up() 和 Down() 方法的迁移类。Up() 是升级脚本,Down() 是回滚脚本,这个对称设计在生产环境救过我一次。实体配置:我更偏爱 Fluent API
EF Core 支持 Data Annotations 和 Fluent API 两种配置方式。注解写在实体上看着直观,但实体一多就脏了,而且像复合索引这种配置注解根本表达不了。我的习惯是每种实体一个配置类:
然后在
OnModelCreating 里一行 modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly) 把程序集中的配置全扫进来,以后新增配置类不用改 DbContext。生产环境怎么执行迁移
千万别在生产服务器上跑 `dotnet ef database update`,这是我在团队里立的第一条规矩。原因很现实:生产机器上不一定有 SDK 和源码,而且命令跑到一半失败时,中间状态很不好排查。
稳妥的做法是把迁移导出成 SQL 脚本,走正常发布流程人工执行:
--idempotent 参数很关键:生成的脚本会先检查 __EFMigrationsHistory 表,已经执行过的迁移自动跳过,同一个脚本反复跑也不会出事。我们的流水线就是构建时生成脚本,审一遍 SQL 再执行。踩过的坑
- 改字段类型丢数据。我把一个
string字段改成int,EF 生成的是ALTER COLUMN,SQL Server 转不过去直接报错;更阴的是某些收窄操作(比如nvarchar(max)改成nvarchar(50))能执行成功,超长数据被静默截断。改类型前一定先备份,并且自己打开迁移文件看看生成的 SQL 到底干了什么。
- 改属性名等于删列加列。直接重命名实体属性,EF 会认为你删了旧列、加了新列,旧列的数据就没了。正确姿势是保留属性映射、用
builder.Property(x => x.NewName).HasColumnName("OldName")过渡,或者手动把生成的迁移改成RenameColumn。
- 迁移历史冲突。两个人在不同分支各加了一个迁移,合并后
ModelSnapshot.cs冲突,迁移的先后关系也可能乱。我们的约定是:合并后删掉自己那个迁移重新生成,别手改快照文件,改坏了比冲突本身更难修。
迁移这东西平时毫无存在感,一出事就是数据层面的事,敬畏之心还是要有的。
Loading...