Caching 如何使用.net-core-2.1中的内存缓存避免缓存未命中

Caching 如何使用.net-core-2.1中的内存缓存避免缓存未命中,caching,asp.net-core-2.0,Caching,Asp.net Core 2.0,如何在后台更新缓存以避免缓存未命中 在.net-core-2.1中,我可以添加一个内存缓存,如下所示: 公共类启动 { public void配置服务(IServiceCollection服务) { services.AddMemoryCache(); } } 然后,使用起来非常简单: [路由(“api”)] 公共类DataController:控制器 { 专用只读IMemoryCache\u缓存; 私有只读数据上下文_DataContext; 公共DataController(IMe

如何在后台更新缓存以避免缓存未命中

在.net-core-2.1中,我可以添加一个内存缓存,如下所示:

公共类启动
{    
public void配置服务(IServiceCollection服务)
{
services.AddMemoryCache();
}
}
然后,使用起来非常简单:

[路由(“api”)]
公共类DataController:控制器
{
专用只读IMemoryCache\u缓存;
私有只读数据上下文_DataContext;
公共DataController(IMemoryCache缓存、DataContext DataContext)
{
_缓存=缓存;
_dataContext=dataContext;
}
[HttpGet]
[路线(“Gimmecachedata”)]
公共异步任务Get()
{
var cacheEntry=wait
_getorCreateAync(“MyCacheKey”,条目=>
{
entry.AbsoluteExpiration=DateTime.Now.AddSeconds(20);
返回Task.FromResult(_dataContext.GetOrders(DateTime.Now));
});
返回Ok(缓存条目);
}
}
然而,正如预期的那样,经过20秒速度惊人的高速缓存的powered bliss infused请求后,缓存项过期,下一个请求由于缓存未命中和后续数据加载而暂停

啊!因此,缓存只在某些时候起作用。为什么不让它一直工作呢

如何将功能添加到:

  • 归还旧物品(同时)和
  • 当项目过期或被通知过期时自动更新缓存,以便下一个请求将获得更新的值
  • 在尝试解决此问题时,我在使用
    IHostedService实现时遇到了两个主要障碍:

  • 当缓存项过期时,它将被逐出,不再可用;意思是我不能退
  • 更新需要数据库的缓存项会导致这些调用超出范围
  • 此缓存更新可以在发现缓存未命中后直接启动,也可以通过主动监视下一个即将过期的项来启动


    我曾尝试使用
    ConcurrentDictionary
    滚动我自己的缓存(将其作为单例添加)。
    CacheItem
    类包含
    到期
    工厂
    (即:值返回委托)的属性。但我发现,由于此委托可能是在请求时设置的,并在
    IHostedService
    后台线程中调用,因此它导致了上下文超出范围异常。

    我找到了一个似乎可行的解决方案

  • 实现IHostedService(从类扩展)。此类将用作.net核心框架管理的后台线程。后台线程将保持缓存更新运行(通过调用
    ICache.UpdateCache
    ,如下所述),以避免请求时缓存命中
  • 公共类CacheUpdateService:BackgroundService
    {
    专用只读ILogger\u记录器;
    私有只读服务器ViceProvider\u服务提供商;
    专用只读ICache\u缓存;
    公共缓存更新服务(ILogger记录器、IServiceProvider服务提供商、ICache缓存)
    {
    _记录器=记录器;
    _服务提供者=服务提供者;
    _缓存=缓存;
    }
    受保护的覆盖异步任务ExecuteAsync(CancellationToken stoppingToken)
    {
    _LogDebug(“CacheUpdateService正在启动”);
    停止通话。登记(处理);
    同时(!stoppingToken.IsCancellationRequested)
    {
    尝试
    {
    使用(var scope=\u serviceProvider.CreateScope())
    {
    var dataContext=scope.ServiceProvider.GetRequiredService();
    //这个紧密循环调用UpdateCache,如果不需要更新,它将被阻止
    等待任务。运行(()=>_cache.UpdateCache(dataContext),stoppingToken);
    }
    }
    捕获(例外情况除外)
    {
    _logger.LogError(例如,“CacheUpdateService中的异常”);
    }
    }
    _LogDebug(“CacheUpdateService已停止”);
    }
    公共覆盖无效处置()
    {
    使用(var scope=\u serviceProvider.CreateScope())
    {
    var scopedProcessingService=scope.ServiceProvider.GetRequiredService();
    //在ICache上此处进行处理将释放任何块
    scopedProcessingService.Dispose();
    }
    base.Dispose();
    }
    }
    
  • 下面的
    Cache
    类实现后台
    UpdateCache
    方法,该方法将一次更新1个过期项目。优先处理最过期的一个。它还实现了请求范围
    GetOrCreate
    方法。注意,我使用
    CacheEntry
    中的委托(
    Func
    )作为值填充工厂。这允许
    Cache
    类插入范围正确的
    DataContext
    (从
    IHostedService
    接收),还允许调用者指定调用
    DataContext
    的哪个方法以获得特定缓存键值的结果。请注意,我正在使用一个
    AutoResetEvent
    来等待第一次数据填充以及启动下一次缓存刷新的计时器。此实现将在第一次调用该项时发生缓存未命中(我猜是在该项未被调用超过1小时之后;因为它将在1小时后被逐出)
  • 公共类缓存条目
    {
    公共字符串密钥{get;set;}
    公共对象值{get;set;}
    公共布尔更新{get;set;}
    公共Int32 ExpirySeconds{get;set;}
    公共日期时间过期{get;set;}
    上次访问的公共日期时间{get;set;}
    public Func ValueFactory{get;set;}
    }
    公共接口ICache:IDisposable
    {
    void UpdateCache(IDataContext数据上下文);
    T GetOrCreate(字符串键,函数工厂,Int32 expirySec