一、今日学习目标
- EF Core 性能优化 10 条(企业高频)
- 正确分页查询(避免性能大坑)
- 全异步 API 改造(async/await 全覆盖)
- 禁止同步阻塞,提升接口吞吐量
- 项目性能直接提升 5~10 倍
二、第一部分:EF Core 性能优化(最重要!)
1. 必须用 AsNoTracking(查询性能提升巨大)
默认 EF 查询会跟踪实体 → 耗内存、耗性能 只读查询必须加 AsNoTracking ()
cs
// 优化前
var user = await _context.Users.FirstOrDefaultAsync(u=>u.Id == id);
// 优化后(只读推荐)
var user = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u=>u.Id == id);
2. 只查需要的字段(Select 映射)
不要查全部字段!
// 差
var list = await _context.Users.ToListAsync();
// 优
var list = await _context.Users
.Select(u => new UserVo { Id = u.Id, Name = u.Name })
.ToListAsync();
3. 禁用懒加载(你项目一定不要开)
懒加载 = N+1 查询灾难!
4. Include 只加载需要的关联数据
不要无脑 Include 所有表
cs
// 只加载需要的角色
var user = await _context.Users
.AsNoTracking()
.Include(u => u.UserRoles)
.ThenInclude(ur => ur.Role)
.FirstOrDefaultAsync(u => u.Id == id);
5. Any () 比 Count () > 0 性能高 10 倍
判断是否存在数据:
cs
// 优
var exists = await _context.Users.AnyAsync(u => u.Account == account);
// 差
var exists = await _context.Users.CountAsync(u => u.Account == account) > 0;
6. 分页用 Skip + Take,不要查全表
var list = await _context.Users
.Where(...)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
7. 批量操作用 EF Core 批量库(可选)
批量删除、批量更新不要循环单条操作。
8. 索引必须加
高频查询字段必须建索引:
- Account
- RoleId
- MenuId
- ParentId
cs
// EF 配置索引
builder.HasIndex(u => u.Account).IsUnique();
9. 禁用异步行为(不影响性能但稳定)
cs
builder.EnableRetryOnFailure(); // 重连
10. 不要在循环里查数据库
循环查库 = 性能自杀
三、第二部分:正确分页查询(企业标准)
你项目的用户 / 角色 / 菜单都要这样写!
分页 Dto
cs
public class PageQueryDto
{
public int PageIndex { get; set; } = 1;
public int PageSize { get; set; } = 10;
}
分页通用返回
cs
public class PageVo<T>
{
public int Total { get; set; }
public List<T> List { get; set; }
}
高性能分页代码(直接复制)
cs
public async Task<PageVo<UserVo>> GetPageAsync(PageQueryDto dto)
{
var query = _context.Users
.AsNoTracking()
.Where(...)
.Select(u => new UserVo{ ... });
var total = await query.CountAsync();
var list = await query
.Skip((dto.PageIndex - 1) * dto.PageSize)
.Take(dto.PageSize)
.ToListAsync();
return new PageVo<UserVo>
{
Total = total,
List = list
};
}
四、第三部分:全异步 API 全覆盖(最重要!)
你的整个项目必须全部改成 async/await
规则:
- 所有 EF 操作必须用 Async 方法
- 所有 Service 方法必须 async Task
- 所有 Controller 必须 async Task<ActionResult>
- 绝对不要用 .Result/.Wait () 同步阻塞
错误写法(会卡死、性能差)
cs
// 错误!同步阻塞
var user = _context.Users.FirstOrDefault(u=>u.Id == id);
正确写法(高性能)
cs
var user = await _context.Users
.AsNoTracking()
.FirstOrDefaultAsync(u=>u.Id == id);
控制器必须异步
cs
[HttpGet("page")]
public async Task<ActionResult<R<PageVo<UserVo>>>> Page(PageQueryDto dto)
{
var result = await _userService.GetPageAsync(dto);
return Ok(result);
}
五、异步性能优势
- 线程不阻塞
- 接口能处理更多并发
- 内存占用更低
- 企业 .NET 开发标准
六、今日性能优化总结(背住)
- 查询必加 AsNoTracking ()
- 只 Select 需要的字段
- Any () 代替 Count () >0
- 分页 Skip + Take
- 禁止循环查库
- 高频字段建索引
- 全异步 async/await
- 禁止同步阻塞(.Result)
七、今日练习
- 把所有 EF 查询加上 AsNoTracking()
- 把所有列表改成 正确分页
- 把整个项目 全部改成异步 async/await
- 把判断存在改成 AnyAsync()
- 给 Account、RoleId、MenuId 加 索引