You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 19:07:36