文章目錄
- 前言
- 一、為什么使用異步方法
- 二、核心異步方法
- 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 實現可控取消
- 避免混合同步/異步代碼