实体更改后更新IMemoryCache

时间:2018-02-12 09:49:25

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

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

我的FromCacheAsync

中的CacheService方法示例
    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));

1 个答案:

答案 0 :(得分: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容器不支持装饰器注册,因此您必须通过工厂模式自己制作或使用支持它的第三方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本身并不“坏”,但使用第三方IoC容器更方便。

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