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

EF Core查询已入库数据需退出应用才刷新问题排查

问题现象

尽管保存操作确实已成功更新数据库中的记录,但客户端调用接口获取玩家列表时始终返回旧数据,必须等用户退出应用重启后,无需额外操作就能正常显示最新的玩家数据。

相关实现代码
  • 客户端调用API获取所有玩家的方法
public async Task<List<Players>> GetALLPlayers()
{
    List<Players> _result = new List<Players>();
    EnsureHttpClientCreated();
    var uri = new Uri(string.Format(Constants.BaseUrl + Constants.ApiSegmant + 
    Constants.GetAllPlayers, string.Empty));

    await AddAuthenicationHeader();
    var response = await httpClient.GetAsync(uri);

    if (response.IsSuccessStatusCode)
    {
       var byteArray = await response.Content.ReadAsByteArrayAsync();

       var content = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
       _result = JsonConvert.DeserializeObject<List<Players>>(content);
    }

    return _result.ToList();
}
  • HttpClient初始化创建逻辑
private HttpClient httpClient;
private HttpClientHandler _httpClientHandler;    

public HttpApi()
{
    _timeout = Timeout ?? TimeSpan.FromSeconds(90);
    CreateHttpClient();
    db = new ThehockeylABSqlLiteDB();
}

private void CreateHttpClient()
{
    _httpClientHandler = new HttpClientHandler
    {
        AutomaticDecompression = DecompressionMethods.Deflate | 
      DecompressionMethods.GZip
    };

    httpClient = new HttpClient(_httpClientHandler, false)
    {
        Timeout = _timeout
    };

  httpClient.DefaultRequestHeaders.UserAgent
  .ParseAdd(ClientUserAgent);

    if (!string.IsNullOrWhiteSpace(Constants.BaseUrl))
    {
        httpClient.BaseAddress = new Uri(Constants.BaseUrl);
    }

    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}

private void EnsureHttpClientCreated()
{
    if (httpClient == null)
    {
        CreateHttpClient();
    }
}
  • 服务端PlayerController的查询接口
private readonly IPlayerInterface db;
public PlayerController(IPlayerInterface classes)
{
   db = classes;
}

[HttpGet("GetALLPlayers")]
[Authorize]
public List<Players> GetALlPlayers()
{
   try
     {
      var test = db.GetAllPlayers();
     }
     catch(Exception ex)
     {

     }

   return db.GetAllPlayers();
}
  • 服务层PlayerService查询实现
public class PlayerService : IPlayerInterface
{
  AppDBContext db;
  public PlayerService(AppDBContext dBContext)
  {
    db = dBContext;
  }

 public List<Players> GetAllPlayers()
 {
   return db.Players.Where(w => w.IsActive == true && w.IsDeleted 
    == false).Include(i => i.Notes).ToList();
 }
// 服务类其余实现省略
}
  • DI服务注册配置
services.AddScoped<IPlayerInterface, PlayerService>();
  • 客户端DataGrid数据绑定逻辑
public async Task<int> BindGrid()
{
    var players =await api.GetALLPlayers();
    dgPlayers.ItemsSource = null;
    dgPlayers.ItemsSource = players; //此处获取的玩家列表仍为旧数据,计数与库中实际数据不符
    playersCount.Text = $"Total Players {players.Count().ToString()}";
    return 1;
}
问题解答

判断结论

你提出的「添加AsNoTracking绕过EF跟踪缓存」的判断是错误的,该方案无法解决当前问题。

根本原因

问题出在客户端HttpClient默认开启的GET响应缓存,和EF Core跟踪缓存无关:

  1. EF Core的跟踪缓存是单个DbContext实例级别的,你当前IPlayerInterface注册为Scoped生命周期,正常情况下默认注册的AppDBContext也是Scoped生命周期,每个HTTP请求都会生成全新的DbContext实例,请求结束后实例会被释放,跨请求不会残留跟踪缓存,因此AsNoTracking不解决这个问题。
  2. 你初始化HttpClientHandler时没有配置缓存策略,.NET的HttpClientHandler默认会对相同URI的GET请求做本地内存缓存:如果服务端响应没有返回明确的禁止缓存头,后续同地址请求会直接返回本地缓存的旧响应,根本不会发起真实的HTTP请求到服务端。当你重启应用后,HttpClient实例被销毁,内存缓存清空,第一次请求自然能拿到最新数据,和你描述的现象完全匹配。

额外提两个代码里的非核心问题:

  • 服务端Controller方法里冗余调用了两次GetAllPlayers(),还吞掉了所有异常,不利于排查问题
  • 可以顺手检查下AppDBContext的DI注册,确保没有被错误注册为Singleton,单例DbContext才会出现全局跟踪缓存导致数据不更新的问题

正确解决方案

客户端侧(快速修复)

修改HttpClient初始化逻辑,显式关闭自动缓存:

_httpClientHandler = new HttpClientHandler
{
    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
    // 新增:彻底关闭HTTP响应缓存
    CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore)
};

也可以直接给HttpClient全局配置默认请求头,强制不使用缓存:

httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue
{
    NoCache = true,
    NoStore = true
};

服务端侧(兜底兼容)

给查询接口增加响应缓存特性,明确告知所有客户端、代理节点不要缓存该接口响应,避免其他类型客户端踩同样的坑:

[HttpGet("GetALLPlayers")]
[Authorize]
// 新增:禁用接口响应缓存
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public List<Players> GetALlPlayers()
{
    // 建议删掉冗余的try块重复查询逻辑,异常处理加日志不要直接吞
    return db.GetAllPlayers();
}

额外优化

针对只读的列表查询场景,你依然可以在EF Core查询里加上AsNoTracking(),这能减少实体跟踪开销、提升查询性能,但这属于性能优化项,不是解决当前旧数据问题的必须手段。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 06:24:30