EF Core 的 Add 方法将全新实体标记为“待插入”,调用 SaveChanges() 后才执行 INSERT sql;它不立即写库,而是设实体状态为 Added,并递归处理导航属性,但需避免对已存在关联实体重复新建。

EF Core 的 Add 方法用于将**全新实体**标记为“待插入”,调用 SaveChanges() 后才会真正写入数据库。它不执行 SQL 插入,只是告诉上下文:“这个对象是新的,下次保存时请插入它”。
基本用法:添加单个实体
最常见场景是创建一个新对象,用 Add 注册,再保存:
- 确保实体类有无参构造函数(或支持初始化属性)
- 不需要手动设置主键(如是自增或 Guid 类型,EF 会自动处理)
- 调用
context.SaveChanges()才真正触发 INSERT SQL
示例:
using (var context = new BloggingContext())
{
var blog = new Blog { Url = “https://example.com” };
context.Blogs.Add(blog);
context.SaveChanges(); // 此时才插入数据库
}
添加带导航属性的实体图
如果新实体关联了其他实体(比如 Blog 带多个 Post),Add 默认会递归处理整个对象图:
- 所有未被上下文跟踪的关联对象也会被标记为
Added - 若关联对象已在数据库中存在(比如已有 User 实体),直接赋值会导致 EF 尝试重复插入——这是常见错误
- 正确做法:对已存在的关联实体,只设外键字段(如
Post.UserId = 123),不要 new 一个 User 再赋给Post.User
举例避坑:
// ❌ 错误:试图重新插入已存在的 User
var post = new Post { Title = “Hello”, User = new User { Id = 123 } };
context.Posts.Add(post); // EF 会把 new User 当作新用户插入
// ✅ 正确:只设外键
var post = new Post { Title = “Hello”, UserId = 123 };
context.Posts.Add(post);
AddAsync 与批量添加
AddAsync 是异步版本,适用于 Web API 或高并发场景,但注意它**不真正异步写库**,只是释放线程等待上下文内部状态变更完成:
- 适合配合
await context.SaveChangesAsync()使用 - 批量添加多个实体时,用
AddRange比循环Add更高效(减少内部状态检查开销)
示例:
var blogs = new[]
{
new Blog { Url = “https://a.com” },
new Blog { Url = “https://b.com” }
};
context.Blogs.AddRange(blogs);
await context.SaveChangesAsync();
和 Attach、Update 的关键区别
别混淆这几个方法的作用:
- Add → 实体状态设为
Added→ 下次 Save 会 INSERT - Attach → 状态设为
Unchanged→ 不参与变更检测,适合只更新部分字段的场景 - Update → 状态设为
Modified→ Save 时所有字段都会 UPDATE(含 NULL 值)
如果只想插入新数据,就只用 Add + SaveChanges,不用混用。
基本上就这些。Add 方法本身很简单,关键是理解它背后的状态机制和导航属性处理逻辑,否则容易在关联数据上踩坑。