实体更改后更新IMemoryCache [英] Updating IMemoryCache once an entity has changed

查看:186
本文介绍了实体更改后更新IMemoryCache的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个CacheService,它使用GetOrCreateAsync创建基于密钥的缓存.我正在缓存具有byte[]属性的照片实体. 这可以很好地缓存并按预期进行检索.但是,如果照片实体已更新,则高速缓存仍会按您期望的那样保留旧实体,因为它尚未过期,如何在保存该实体时强制对高速缓存进行更新?我是否要删除现有的缓存实体并重新添加更新的实体?

I have a CacheService that uses GetOrCreateAsync to create cache based on a key. I am caching a photograph entity, which has a byte[] property. This caches fine and is retrieved as expected. However if the photograph entity is updated, the cache still retains the old entity as you would expect because it has not expired, how can I force an update to the cache upon save of this entity? Do I remove the existing cached entity and re-add the updated one?

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));

推荐答案

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

You need to invalidate the cache key, when the entity changes.

如果直接在DbContext上进行操作,则可能会有些棘手.但是由于您使用的是存储库模式,因此更容易做到.

That may be a bit tricky, if you directly operate on the DbContext. But since you are using repository pattern, that`s easier to do.

它归结为将IMemoryCache注入到您的存储库中,并在更新图片时使它无效.

It boils down to inject the IMemoryCache into your repository and invalidate it when a picture is updated.

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容器.

The catch is, the built-in DI/IoC container doesn't support decorator registrations, so you'll have to make it yourself via factory pattern or use a 3rd party IoC container which supports it.

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容器则更加方便.

It's per se not "bad" to use new within the composition root (where you configure your DI/IoC container), but with 3rd party IoC container its just more convenient.

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

Of course you can also register PhotographRepository with the IoC container and have it resolved. But that would also allow you to inject PhotographRepository into your services whereas the above prevents it, because only the IPhotographRepository interface is registered.

这篇关于实体更改后更新IMemoryCache的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆