Entity Framework Core 是“边建模边理解”的工具,核心痛点是模型约定与 DbContext 生命周期:必须注册为 Scoped 而非 Singleton,主键需显式配置,迁移需正确引用模型,自增主键插入需避免手动赋值。

Entity Framework Core 不是“先学再用”的框架,而是“边建模边理解”的工具。如果你刚创建完第一个 DbContext 却发现查询返回空、迁移不生成表、或 SaveChanges() 报错,说明你卡在了模型约定和生命周期这两个真实痛点上。
为什么 DbContext 实例不能全局单例?
EF Core 的 DbContext 不是线程安全的,也不是轻量对象。它内部维护了变更跟踪器(ChangeTracker)、缓存、以及当前连接状态。若在 ASP.NET Core 中注册为 Singleton,多个请求会共享同一实例,导致实体状态混乱、并发修改异常,甚至出现“An instance of entity type 'X' cannot be tracked”这类错误。
- ASP.NET Core 中必须注册为
Scoped:services.AddDbContext
(options => options.UseSqlServer(connectionString)); - 手动 new 出来的
DbContext必须显式Dispose(),尤其在非 Web 环境(如 Console 或 BackgroundService)中 - 不要在静态字段或单例服务里持有
DbContext实例
DbSet 查询返回空,但数据库明明有数据?
最常见原因是未正确配置主键或忽略大小写/类型匹配。EF Core 默认按命名约定推断主键:类名为 User,就找 Id 或 UserId。如果实体是 public class user { public int id { get; set; } }(小写),EF Core 会认为没有主键,整个 DbSet 被跳过映射。
- 加
[Key]特性或在OnModelCreating中显式配置:modelBuilder.Entity
().HasKey(e => e.id); - 检查字段类型是否与数据库一致:C# 的
DateTime对应 SQL Server 的datetime2,不是datetime;string默认映射为nvarchar(max),若数据库是varchar(50),需用[Column(TypeName = "varchar(50)")] - 使用
context.Set只读查询时,不会触发变更跟踪,但不影响数据读取——空结果通常不是因为它().AsNoTracking()
add-migration 什么都不生成?
dotnet ef migrations add 命令只对比当前模型与 Migrations 目录下最后一个快照(*.Designer.cs 文件)。如果没生成任何代码,大概率是以下三种情况之一:
- 项目未安装
Microsoft.EntityFrameworkCore.Tools包,或未在.csproj中启用设计时支持:net8.0 enable enable false - 上下文类未继承自
DbContext,或构造函数未接收DbContextOptions - 模型类未在
OnModelCreating或DbSet属性中被引用(例如漏写了public DbSet)Products { get; set; }
SaveChanges() 报错 “Cannot insert explicit value for identity column”?
这是 EF Core 对自增主键(SQL Server 的 IDENTITY、PostgreSQL 的 SERIAL)的保护机制。当你给一个标识列赋了值(比如 new Product { Id = 100, Name = "A" }),EF Core 默认认为你想显式插入该值,而数据库不允许。
-
解决方法一:移除赋值,让数据库生成:
var p = new Product { Name = "A" }; // 不设 Id - 解决方法二:若真需要指定 ID(如迁移旧数据),需在模型中关闭值生成:
modelBuilder.Entity
() .Property(e => e.Id) .ValueGeneratedNever(); - 注意:MySQL 的
AUTO_INCREMENT列默认也受此规则约束,不能靠HasDefaultValueSql("nextval('seq')")绕过
EF Core 的“约定优于配置”省事,但也意味着出问题时很难一眼看出哪条约定被打破了。比起死记文档,更有效的方式是打开 DbContext 的日志输出,看它实际生成的 SQL 和模型解析过程——那才是你真正该调试的地方。










