文章目录
- 前言
- 一、为什么使用异步方法
- 二、核心异步方法
- 1)查询数据
- 2)保存数据
- 3)事务处理
- 三、异步查询最佳实践
- 1)始终使用 await
- 2)组合异步操作
- 3)并行查询(谨慎使用)
- 四、异常处理
- 五、性能注意事项
- 1)DbContext 生命周期
- 2)取消操作支持
- 3)禁用跟踪(只读场景)
- 六、常见错误
- 1)混合同步/异步调用
- 2)未正确处理上下文
- 七、高级模式
- 1)批量操作
- 2)原始 SQL 查询
- 总结
- EF Core 的异步方法通过以下方式提升性能:
- 关键要点:
前言
在 Entity Framework Core 中,异步方法是优化数据库操作性能、避免阻塞线程的关键工具,特别适用于 Web 应用、API 或其他高并发场景。
一、为什么使用异步方法
- 避免线程阻塞:异步操作释放当前线程(如 ASP.NET 的请求线程),提高吞吐量。
- 提升响应性:在 UI 应用(如 WPF、MAUI)中防止界面卡死。
- 高效利用资源:适合长时间运行的数据库操作(如复杂查询、批量写入)。
二、核心异步方法
EF Core 为常见操作提供了异步版本,需结合 async/await 使用:
1)查询数据
- ToListAsync():异步返回列表
- FirstOrDefaultAsync():异步获取首个匹配项
- SingleOrDefaultAsync():异步获取唯一匹配项
- CountAsync():异步统计数量
- AnyAsync():异步检查是否存在
public async Task<List<Person>> GetActivePersonsAsync()
{using var context = new MyDbContext();return await context.Persons.Where(u => u.Name=="Tom").ToListAsync(); // 异步执行查询
}
2)保存数据
- SaveChangesAsync():异步提交更改
- AddAsync():异步添加单个实体(通常用于值生成策略)
public async Task CreatePersonsAsync(Person person)
{using var context = new MyDbContext();await context.Persons.AddAsync(person); // 异步添加await context.SaveChangesAsync(); // 异步提交
}
3)事务处理
public async Task TransferMoneyAsync(int fromId, int toId, decimal amount)
{using var context = new MyDbContext();using var transaction = await context.Database.BeginTransactionAsync();try{var fromAccount = await context.Accounts.FindAsync(fromId);var toAccount = await context.Accounts.FindAsync(toId);fromAccount.Balance -= amount;toAccount.Balance += amount;await context.SaveChangesAsync();await transaction.CommitAsync();}catch{await transaction.RollbackAsync();throw;}
}
三、异步查询最佳实践
1)始终使用 await
// ✅ 正确
var persons = await context.Persons.ToListAsync();// ❌ 错误(立即阻塞线程)
var persons = context.Persons.ToListAsync().Result;
2)组合异步操作
public async Task<Person> GetPersonWithOrdersAsync(int personId)
{return await context.Persons.Include(u => u.Orders).FirstOrDefaultAsync(u => u.Id == personId);
}
3)并行查询(谨慎使用)
var task1 = context.Persons.CountAsync();
var task2 = context.Orders.CountAsync();await Task.WhenAll(task1, task2);var totalPersons = task1.Result;
var totalOrders = task2.Result;
四、异常处理
try
{await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{// 处理并发冲突
}
catch (DbUpdateException ex)
{// 处理更新错误
}
五、性能注意事项
1)DbContext 生命周期
- 确保在 using 块或依赖注入范围内使用,避免内存泄漏。
2)取消操作支持
-
多数异步方法接受 CancellationToken:
var cts = new CancellationTokenSource(); var users = await context.Persons.ToListAsync(cts.Token);
3)禁用跟踪(只读场景)
var persons = await context.Persons.AsNoTracking().ToListAsync();
六、常见错误
1)混合同步/异步调用
// ❌ 危险!可能导致死锁
public Person GetPerson(int id)
{return context.Persons.FirstOrDefaultAsync(u => u.Id == id).Result;
}
错误修正(修正为异步方法)
- 始终优先使用 async/await,避免 .Result 或 .Wait()
public async Task<Person> GetPersonAsync(int id)
{return await context.Persons.FirstOrDefaultAsync(u => u.Id == id);
}
2)未正确处理上下文
// ❌ 上下文可能被提前释放
public async Task<List<Person>> GetPersonsAsync()
{using var context = new MyDbContext();return await context.Persons.ToListAsync();
} // 上下文在此处释放,返回的实体可能无法延迟加载
错误修正(将原始代码改为安全模式)
- 优先使用 DTO 或投影
避免直接返回实体,尤其是在 Web API 中。 - 明确加载关联数据
使用 Include 或 Select 确保所有需要的数据已加载。 - 默认禁用跟踪
在只读场景中,始终使用 AsNoTracking 提升性能。 - 严格管理 DbContext 生命周期
Web 应用中,通过依赖注入(Scoped 生命周期)管理 DbContext。
桌面应用中,确保 DbContext 生命周期与 UI 操作同步。
// ✅ 安全:返回 DTO,无需延迟加载
public async Task<List<PersonDto>> GetPersonsAsync()
{using var context = new MyDbContext();return await context.Persons.Select(p => new PersonDto {Id = p.Id,Name = p.Name}).AsNoTracking().ToListAsync();
}
七、高级模式
1)批量操作
await context.BulkInsertAsync(entities); // 使用 EF Plus 等扩展库
2)原始 SQL 查询
var persons = await context.Persons.FromSqlInterpolated("SELECT * FROM T_Persons WHERE Age > {0}", 18).ToListAsync();
总结
EF Core 的异步方法通过以下方式提升性能:
- 减少线程阻塞
- 提高服务器吞吐量
- 优化资源利用率
关键要点:
- 始终 await 异步方法
- 正确处理上下文生命周期
- 结合 CancellationToken 实现可控取消
- 避免混合同步/异步代码