如何优化.NET 7 API端点性能、扩展性与高负载处理能力
.NET 7 应用性能优化建议
背景
我有一个.NET 7应用,每日处理数千用户请求,高峰时段会耗尽运行所在IIS服务器的资源。目前希望先最大化利用现有资源,暂不考虑容器化。作为开发新手,无计算机科学背景,对异步或多线程了解有限。
举例场景:某端点每秒被调用约300次,后端数据库为SQL Server 2019(标准版),使用Entity Framework Code First,通过.NET Identity进行端点认证。
以下针对两段核心代码提供优化建议,兼顾高并发处理能力与服务器资源效率:
一、「获取结果」接口优化
原代码问题
- 同步EF查询阻塞线程,无法高效处理高并发
- 多次数据库往返,增加延迟与数据库负载
- 内存中遍历过滤效率低
- 日期解析不安全,未处理时区问题
优化后代码
[Route("/mobile/result/{matchtype}/{referenceid}/{lastchecked}")] public async Task<IActionResult> GetResult(MatchType matchType, long referenceid, string lastchecked) { // 1. 安全解析日期并统一为UTC(也可直接将参数改为DateTime类型,让ASP.NET自动绑定) if (!DateTime.TryParse(HttpUtility.UrlDecode(lastchecked), out DateTime datetocheck)) return BadRequest("无效的日期格式"); datetocheck = DateTime.SpecifyKind(datetocheck, DateTimeKind.Utc); if (matchType == MatchType.League) { // 2. 异步查询+关闭实体跟踪(只读场景减少EF上下文开销) var response = await _context.Results .AsNoTracking() .Where(i => i.Fixture.Id == referenceid && i.LastUpdated >= datetocheck) .Select(i => new Result() { ReferenceId = referenceid, MatchType = matchType, ResultId = i.Id, HomeFrameScore = i.HomeScore, AwayFrameScore = i.AwayScore, HomeHandicap = i.HomeTeam.Handicap, AwayHandicap = i.AwayTeam.Handicap, MatchStatus = i.Status }) .FirstOrDefaultAsync(); if (response == null) return NoContent(); // 3. 合并查询:一次性获取Frames和关联玩家数据,减少数据库往返 var framesWithPlayers = await _context.ResultFrames .AsNoTracking() .Where(i => i.Result.Fixture.Id == referenceid) .Select(i => new { Frame = new Frame() { ResultFrameId = i.Id, FrameNumber = i.FrameNumber, HomeBreak = i.HomeBreak, HomeDish = i.HomeDish, HomeForfeit = i.HomeForfeit, HomeLockedIn = i.HomeLockedIn, HomeScore = i.HomeScore, AwayBreak = i.AwayBreak, AwayDish = i.AwayDish, AwayForfeit = i.AwayForfeit, AwayLockedIn = i.AwayLockedIn, AwayScore = i.AwayScore, Status = i.FrameStatus, MatchFormatSectionId = i.MatchFormatSection.Id }, Players = _context.ResultFramePlayers .AsNoTracking() .Where(p => p.ResultFrame.Id == i.Id) .Select(p => new FramePlayer() { ResultFramePlayerId = p.Id, Number = p.Number, ResultFrameId = p.ResultFrame.Id, Joker = p.Joker, HomeAway = p.HomeAway, PlayerId = p.PlayerSeasonTeam != null ? p.PlayerSeasonTeam.Player.Id : 0 }) }) .ToListAsync(); // 4. 直接映射关联关系,避免内存中反复遍历过滤 response.Frames = framesWithPlayers.Select(fwp => { fwp.Frame.HomeFramePlayers = fwp.Players.Where(p => p.HomeAway == HomeAway.Home).ToList(); fwp.Frame.AwayFramePlayers = fwp.Players.Where(p => p.HomeAway == HomeAway.Away).ToList(); return fwp.Frame; }).ToList(); } return Json(response); }
优化点解释
- 异步EF方法:用
FirstOrDefaultAsync/ToListAsync替代同步方法,释放线程处理更多请求,提升并发能力 - AsNoTracking:只读场景关闭EF的实体跟踪,减少内存占用与上下文开销
- 合并查询:将Frames和Player的查询合并为一次数据库请求,减少网络延迟与数据库压力
- 安全日期解析:用
TryParse避免异常,统一UTC时间避免时区混乱 - 内存操作优化:直接在查询投影中关联玩家数据,避免后续反复遍历过滤
二、「锁定分段」接口优化
原代码问题
- 循环中多次查询数据库,产生N+1性能问题
- 同步查询阻塞线程
- 加载冗余数据(不必要的
Include) - 手动调用
Update(EF上下文已跟踪实体,无需额外操作)
优化后代码
[Route("/mobile/lockinsection")] [HttpPost] public async Task<IActionResult> LockInSection([FromBody] FrameResultData data) { var user = await _userManager.GetUserAsync(User); if (data.MatchType == MatchType.League) { // 1. 验证方法异步化(内部用EF查询必须改为异步) var checkcaptain = await ValidCaptainAsync(data.MatchType, data.ReferenceId); if (!checkcaptain) return BadRequest("无权限操作:非本场队长"); var captainhomeaway = await CaptainHomeOrAwayAsync(data.MatchType, data.ReferenceId); if (data.LockIn.HomeAway != captainhomeaway) return BadRequest("操作无效:只能锁定自己队伍的分段"); // 2. 批量查询需要更新的ResultFrame,避免循环查询 var frameIds = data.LockIn.Frames.Select(f => f.ResultFrameId).ToList(); var resultFrames = await _context.ResultFrames .Where(i => frameIds.Contains(i.Id)) .ToListAsync(); // 3. 批量更新实体(EF自动跟踪状态变化,无需调用Update) foreach (var frame in resultFrames) { if (data.LockIn.HomeAway == HomeAway.Home) frame.HomeLockedIn = LockedIn.True; else frame.AwayLockedIn = LockedIn.True; } // 4. 异步更新LastUpdated var result = await _context.Results .FirstOrDefaultAsync(i => i.Fixture.Id == data.ReferenceId); if (result == null) return NotFound("未找到匹配记录"); result.LastUpdated = DateTime.UtcNow; // 5. 异步保存所有变更(原子操作,确保全成功或全失败) await _context.SaveChangesAsync(); return Ok(); } else { // 赛事逻辑实现 return BadRequest("暂不支持该赛事类型"); } } // 异步验证方法示例 private async Task<bool> ValidCaptainAsync(MatchType matchType, long referenceId) { return await _context.TeamCaptains .AnyAsync(c => c.MatchType == matchType && c.FixtureId == referenceId && c.UserId == _userManager.GetUserId(User)); } private async Task<HomeAway> CaptainHomeOrAwayAsync(MatchType matchType, long referenceId) { var captain = await _context.TeamCaptains .FirstOrDefaultAsync(c => c.MatchType == matchType && c.FixtureId == referenceId && c.UserId == _userManager.GetUserId(User)); return captain?.HomeAway ?? HomeAway.Home; }
优化点解释
- 批量查询:一次性获取所有需要更新的ResultFrame,避免循环中反复查询数据库
- 全异步化:包括验证方法、查询、保存操作,提升线程利用率
- 移除冗余Include:原代码中
Include(i=>i.Result)未被使用,直接删除减少数据加载 - 避免手动Update:EF上下文会跟踪实体属性变化,直接修改属性即可
- 原子保存:
SaveChangesAsync默认是事务,确保所有更新和LastUpdated的修改同时生效 - 增强参数校验:增加空值判断,提升接口健壮性
内容的提问来源于stack exchange,提问作者Matthew Warr
相关产品推荐
相关产品推荐

