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

.NET Core API单请求内无Session临时存储对象列表的最佳实践

请求周期内复用数据的无Session方案(.NET Core + AWS EKS)

针对你单个请求内大量重复SQL调用的问题,以下是不需要Session的可行方案和最佳实践:

1. 请求级Scoped服务缓存

自定义一个简单的缓存类,比如RequestScopedCache,内部用ConcurrentDictionary<string, object>存储数据,然后在Program.cs里将其注册为Scoped生命周期:

services.AddScoped<RequestScopedCache>();

各个模块需要数据时,先通过这个Scoped实例查询缓存键(可通过SQL语句+参数的组合生成唯一键,比如$"Product_{productId}"),如果未找到则执行SQL查询,将结果存入缓存。由于Scoped服务在单个请求内是唯一实例,请求结束后会被自动回收,既不会跨请求污染数据,也不会像静态类那样占用容器全局内存。

2. 直接使用HttpContext.Items

HttpContext.Items是.NET Core内置的请求级键值对存储,天然绑定当前请求,请求完成后自动释放资源。用法简单直接:

var cacheKey = $"User_{userId}";
if (!HttpContext.Items.ContainsKey(cacheKey))
{
    var user = await _dbContext.Users.FindAsync(userId);
    HttpContext.Items[cacheKey] = user;
}
var cachedUser = (User)HttpContext.Items[cacheKey];

这种方式无需额外定义类,适合简单场景,唯一需要注意的是要统一规范缓存键,避免键冲突。

3. 封装通用查询缓存方法

在Repository或基础服务层封装一个通用的GetOrFetchAsync方法,统一处理请求内的缓存逻辑,避免业务代码重复:

public async Task<T> GetOrFetchAsync<T>(string cacheKey, Func<Task<T>> fetchFunc)
{
    if (_requestCache.TryGetValue(cacheKey, out var cachedValue))
    {
        return (T)cachedValue;
    }
    var result = await fetchFunc();
    _requestCache.TryAdd(cacheKey, result);
    return result;
}

业务模块只需调用该方法,传入缓存键和查询逻辑即可,无需关心缓存细节,代码更简洁易维护。

4. 从根源减少重复查询

上述方法是“治标”,更关键的是优化SQL本身,从根源减少重复调用:

  • 将循环内的单条查询改为批量查询:比如原本循环100次查询单条用户数据,改成SELECT * FROM Users WHERE Id IN (@ids)一次性拉取所有需要的数据,之后在内存中按需取用,直接将数据库调用从100次降至1次。
  • 排查N+1问题:如果使用EF Core,检查是否存在未提前关联的查询,用Include/ThenInclude或者AsSplitQuery一次性加载关联数据,避免后续循环中反复查询关联表。

5. EKS环境下的注意事项

  • 容器化部署中,请求级缓存是完全隔离的,每个请求的缓存数据不会共享,请求结束后资源自动释放,不会增加容器的长期负载,无需担心内存泄漏问题。
  • 绝对不要用静态类或Singleton服务存储请求级数据,这会导致多请求间的数据污染,还会长期占用容器内存,升高负载。

内容的提问来源于stack exchange,提问作者Subrata

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:15:44