【问题标题】:Updating IMemoryCache once an entity has changed实体更改后更新 IMemoryCache
【发布时间】:2018-02-13 03:35:31
【问题描述】:

我有一个CacheService,它使用GetOrCreateAsync 根据一个键创建缓存。我正在缓存一个具有byte[] 属性的照片实体。 这可以很好地缓存并按预期检索。但是,如果更新了照片实体,缓存仍然会保留旧实体,因为它尚未过期,如何在保存此实体时强制更新缓存?我是否要删除现有的缓存实体并重新添加更新的实体?

我的CacheService 中的FromCacheAsync 方法示例

    public async Task<T> FromCacheAsync<T>(string entityName, int clientId, Func<Task<T>> function)
    {
        string cacheKey = GetClientCacheKey(entityName, clientId, function);

        if (!_cache.TryGetValue(cacheKey, out T entry))
        {
            async Task<T> factory(ICacheEntry cacheEntry)
            {
                return await function();
            }
            return await _cache.GetOrCreateAsync(cacheKey, factory);
        }

        return entry;
    }

这是一个使用缓存的例子。

      var existingPhotograph = await _cacheService.FromCacheAsync(nameof(_context.Photograph), clientId, async () =>
            await _photographRepository.GetByStaffIdAsync(staff.StaffId));

【问题讨论】:

  • 请解释投反对票的原因。

标签: c# entity-framework caching asp.net-core asp.net-core-mvc


【解决方案1】:

当实体更改时,您需要使缓存键无效。

这可能有点棘手,如果你直接在 DbContext 上操作的话。但是由于您使用的是存储库模式,所以这样做更容易。

归结为将IMemoryCache 注入您的存储库并在图片更新时使其失效。

public class PhotographRepository : IPhotograpRepository
{
    private readonly IMemoryCache _cache;
    public PhotographReposiory(IMemoryCache cache, ...)
    {
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
    }

    public async Task Update(PhotographEntity entity)
    {
        // update your entity here
        await _context.SaveChangesAsync();

        // this invalidates your memory cache. Next call to _cache.TryGetValue
        // results in a cache miss and the new entity is fetched from the database
        _cache.Remove(GetClientCacheKey(entityName, clientId));
    }
}

使用装饰模式

public class PhotographRepository : IPhotograpRepository
{
    private readonly ApplicationDbContext _context;
    public PhotographReposiory(ApplicationDbContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public async Task Update(PhotographEntity entity)
    {
        // update your entity here
        await _context.SaveChangesAsync();
    }
}


public class CachedPhotographRepository : IPhotograpRepository
{
    private readonly IMemoryCache _cache;
    private readonly IPhotograpRepository _repository;
    public CachedPhotographRepository(IPhotograpRepository repository, IMemoryCache cache)
    {
        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        _repository = _repository ?? throw new ArgumentNullException(nameof(repository));
    }

    public async Task Update(PhotographEntity entity)
    {
        // do the update in the passed in repository
        await _repository.Update(entity);

        // if no exception is thrown, it was successful
        _cache.Remove(GetClientCacheKey(entityName, clientId));
    }
}

问题是,内置的 DI/IoC 容器不支持装饰器注册,因此您必须通过工厂模式自己制作或使用支持它的第 3 方 IoC 容器。

services.AddScoped<IPhotograpRepository>(provider =>
    // Create an instance of PhotographRepository and inject the memory cache
    new CachedPhotographRepository(
        // create an instance of the repository and resolve the DbContext and pass to it
        new PhotographRepository(provider.GetRequiredService<ApplicationDbContext>()),
        provider.GetRequiredService<IMemoryCache>()
    )
);

在组合根(配置 DI/IoC 容器的地方)中使用 new 本身并不是“坏事”,但使用第 3 方 IoC 容器更方便。

当然,您也可以在 IoC 容器中注册 PhotographRepository 并解决它。但这也允许您将PhotographRepository 注入到您的服务中,而上面的方法阻止了它,因为只注册了IPhotographRepository 接口。

【讨论】:

  • 谢谢,这就是我的想法,但我不太确定在我的存储库模式周围使用 IMemoryCache 是否是一种好习惯,但在这里使用它确实很有意义。跨度>
  • 没错。但是为了简单地展示如何做到这一点,它已经足够好了。在实践中,您可能希望使用Decorator Pattern 并创建一个PhotographRepository 和一个CachedPhotographRepository(注入IPhotographRepositoryPhotographRepository)并包装调用。只是忙于写那个大答案哈哈
  • 这是有道理的,提供的答案在我的场景中不起作用,因为我使用通用存储库来更新实体,所以很遗憾不能将它包装在缓存中,但答案本身真的很有帮助,谢谢,当您有空时,是否可以向我展示您上面的意思,因为这是我以前使用通用存储库没有做过的事情。除非我在我的照片存储库中创建一个不使用从我的通用存储库继承的更新方法的新方法,但这并不理想并且将来可能会造成混淆?谢谢!
  • 还添加了装饰器模式,但请记住,它不是原始问题的一部分,下次可能更适合新问题
猜你喜欢
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 2019-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
相关资源
最近更新 更多