891 字
4 分钟
深入浅出 EF Core 的 CRUD 操作(增删改查)
2017-10-18

Entity Framework Core (EF Core) 作为 .NET 开发者最常用的 ORM 框架,其核心魅力在于将复杂的 SQL 操作抽象为对内存中“实体对象”的操作。本文将带大家深入了解如何执行高效的增删改查(CRUD),并剖析其底层的状态管理机制

一、 EF Core 核心:实体状态机#

在深入操作前,必须理解 EF Core 是如何感知数据变化的。每一个被 DbContext 加载的实体都有一个 EntityState

  • Detached:对象已创建,但尚未被 DbContext 跟踪。
  • Added:对象在内存中是新的,数据库中尚不存在,调用 SaveChanges 后将执行 INSERT
  • Unchanged:对象与数据库一致,无需操作。
  • Modified:对象属性被修改,调用 SaveChanges 后将执行 UPDATE
  • Deleted:对象被标记删除,调用 SaveChanges 后将执行 DELETE

二、 数据新增操作 (Create)#

使用 EF Core 插入数据时,推荐始终使用异步方法以提高系统吞吐量。

public async Task InsertAsync()
{
using var context = new MyDbContext();
// 1. 业务逻辑判断
if (await context.TestTables.AnyAsync(p => p.Id == 1)) return;
// 2. 构造实体(此时状态为 Detached)
var newEntity = new TestTable
{
Name = "新数据条目"
};
// 3. 将状态标记为 Added
await context.TestTables.AddAsync(newEntity);
// 4. 持久化(将内存变更转换为 SQL 事务执行)
await context.SaveChangesAsync();
}

⚡ 避坑指南: > * 不要在方法签名中使用 async void(除非是事件处理),应使用 async Task

  • AddAsync 仅在特殊场景(如使用某些 HiLo 自增序列)才真正需要异步。对于大多数情况,直接使用 context.Add() 也是可以的,因为它只操作内存。


三、 数据查询操作 (Read)#

EF Core 提供了跟踪查询(默认)与非跟踪查询

1. 跟踪查询 (Tracking)#

默认情况下,EF Core 会在“快照”中记录查询到的实体。

var entities = await context.TestTables.ToListAsync();

2. 非跟踪查询 (No-Tracking)#

如果你只是展示数据,不打算修改,必须使用 AsNoTracking()。它能显著降低内存开销并提升查询速度,因为它跳过了状态跟踪逻辑。

var readOnlyList = await context.TestTables
.AsNoTracking()
.Where(p => p.Id > 10)
.ToListAsync();

四、 数据更新操作 (Update)#

EF Core 的更新是基于“变化检测”的。

public async Task UpdateAsync(int id)
{
using var context = new MyDbContext();
// 1. 先追踪:从数据库获取原始数据
var entity = await context.TestTables.FindAsync(id);
if (entity != null)
{
// 2. 修改属性:EF Core 会自动将状态改为 Modified
entity.Name = "更新后的名称";
// 3. 提交:仅会更新发生变化的列
await context.SaveChangesAsync();
}
}

💡 原理小贴士:当调用 SaveChangesAsync 时,EF Core 会对比当前实体与查询时的“初始快照”,只针对有差异的字段生成 UPDATE 语句。


五、 数据删除操作 (Delete)#

1. 常规删除(查 -> 删)#

var entity = await context.TestTables.FindAsync(id);
if (entity != null)
{
context.TestTables.Remove(entity);
await context.SaveChangesAsync();
}

2. 高级技巧:无查询删除(仅通过主键)#

如果你已知 ID,且不想额外消耗一次数据库查询请求,可以伪造一个“已附加”的对象:

var entity = new TestTable { Id = 1 }; // 仅需主键
context.Entry(entity).State = EntityState.Deleted; // 直接标记状态
await context.SaveChangesAsync();

六、 总结与最佳实践#

为了写出高性能、可维护的 EF Core 代码,请参考以下建议:

操作最佳实践
查询纯展示页面务必带上 .AsNoTracking()
性能批量操作(如一次增删上千条)应考虑 ExecuteUpdateAsyncExecuteDeleteAsync(EF Core 7.0+ 引入)。
事务SaveChangesAsync 默认带有事务。如需跨 Context 事务,请使用 IDbContextTransaction
异步始终优先使用 ToListAsyncFirstOrDefaultAsync 等异步扩展方法。
深入浅出 EF Core 的 CRUD 操作(增删改查)
https://sw.rscclub.website/posts/efcrud/
作者
杨月昌
发布于
2017-10-18
许可协议
CC BY-NC-SA 4.0