Code First是以C#实体类为唯一数据模型源头、由EF Core自动推导数据库结构的开发模式,需遵守属性可写性、主键命名、导航属性virtual等约束,通过Fluent API配置关系,严格按三步执行迁移,并禁用EnsureCreated()。

Code First 不是“先写代码再生成数据库”的简单流程,而是以 C# 类(实体)为唯一数据模型源头,由 EF Core 自动推导并管理数据库结构的开发模式。它要求你放弃手动建表思维,把注意力放在领域对象设计和迁移生命周期上。
定义实体类时必须注意的三个约束
EF Core 会根据约定自动映射属性,但一旦违反默认规则,就会导致迁移失败或运行时报错 InvalidOperationException。
-
public属性且有get和set访问器才会被识别为字段;private set可接受,但readonly字段或只读属性不会映射 - 主键命名必须是
Id或{ClassName}Id(如UserId),否则需用[Key]显式标记 - 导航属性(如
public ICollection)必须声明为Orders { get; set; } virtual(启用延迟加载时)或使用Include()显式加载,否则查询时为空
DbContext 子类中如何正确配置关系
仅靠属性命名无法表达所有关系类型(如一对多、多对多、自引用)。必须重写 OnModelCreating 方法,用 Fluent API 补充约定之外的逻辑。
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasOne(o => o.User)
.WithMany(u => u.Orders)
.HasForeignKey(o => o.UserId);
modelBuilder.EntityzuojiankuohaophpcnUseryoujiankuohaophpcn()
.HasIndex(u =youjiankuohaophpcn u.Email)
.IsUnique();
modelBuilder.EntityzuojiankuohaophpcnPostTagyoujiankuohaophpcn()
.HasKey(pt =youjiankuohaophpcn new { pt.PostId, pt.TagId });}
常见疏漏:HasOne().WithMany() 忘记配对反向导航属性,或在多对多中间表中没用 HasKey 显式指定复合主键,会导致迁移生成错误的外键约束。
执行迁移时最容易出错的三步操作
迁移不是“一键生成”,而是一系列可审查、可回滚的变更脚本。跳过其中任意一步,都可能让本地数据库与代码模型脱节。
- 修改实体后,必须先运行
dotnet ef migrations add InitialCreate(或带描述的名称),生成 Migrations/xxx_InitialCreate.cs —— 这个文件不能手动改,它是 EF Core 推导出的“差量”
- 检查生成的
Up(MigrationBuilder migrationBuilder) 方法是否符合预期,比如字段类型是否正确(string 默认映射为 nvarchar(max),常需用 HasMaxLength(50) 限制)
- 执行
dotnet ef database update 才真正应用变更;若想回退,用 dotnet ef database update PreviousMigrationName,而不是删掉迁移文件
生产环境禁用 Database.EnsureCreated()
这个方法会直接删库重建,在开发阶段看似方便,但一旦误部署到测试或生产环境,后果不可逆。
替代方案只有且必须是迁移:
- 首次部署:运行
dotnet ef database update 应用全部待迁移
- 后续更新:每次发布前生成新迁移,上线时执行
update 到最新版本
- 若需初始化空库,可用
dotnet ef migrations script --idempotent 生成兼容性脚本,供 DBA 审核执行
真正难的不是写第一个 DbContext,而是让团队所有人理解:迁移文件是代码的一部分,要进 Git、要 Code Review、要和实体变更保持原子性 —— 否则迟早遇到 Microsoft.Data.SqlClient.SqlException: Invalid object name 'Users' 这类运行时错误。










