【问题标题】:C# IUnitOfWork, IRepository and IMemoryCache [ASP.Net Core]C# IUnitOfWork、IRepository 和 IMemoryCache [ASP.Net 核心]
【发布时间】:2018-08-20 22:05:59
【问题描述】:

我的目标是缓存我的数据。我使用AspBoilerPlate 作为我代码的灵感来源。

我的 WebApi 使用 Asp.net Core。

GitHub

我在我的 GitHub 上创建了一个分支以获取更多详细信息:

CacheRepository

public class CacheRepository<TEntity, TPrimaryKey> : Repository<TEntity, TPrimaryKey>, ICacheRepository<TEntity, TPrimaryKey>
  where TEntity : class, IEntity<TPrimaryKey>
{
    private readonly IMemoryCache _memoryCache;

    public CacheRepository(DbContext dbContext, IMemoryCache memoryCache)
        : base(dbContext)
    {
        _memoryCache = memoryCache;
    }

    public override TEntity FirstOrDefault(TPrimaryKey id)
    {
        if (!_memoryCache.TryGetValue(GetCacheKey(id), out TEntity result))
        {
            result = base.FirstOrDefault(id);
            if (result != null)
            {
                PutInCache(result);
            }
        }

        return result;
    }

    public override void Delete(Expression<Func<TEntity, bool>> predicate)
    {
        foreach (var entity in GetAll().Where(predicate).ToList())
        {
            Delete(entity);
        }
    }

    public override void Delete(TEntity entity)
    {
        base.Delete(entity);
        RemoveFromCache(entity.Id);
    }

    public override void Delete(TPrimaryKey id)
    {
        base.Delete(id);
        RemoveFromCache(id);
    }

    public override Task DeleteAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return Task.Run(() => Delete(predicate));
    }

    public override Task DeleteAsync(TEntity entity)
    {
        return Task.Run(() => Delete(entity));
    }

    public override Task DeleteAsync(TPrimaryKey id)
    {
        return Task.Run(() => Delete(id));
    }

    public override Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
    {
        return _memoryCache.GetOrCreateAsync(
            GetCacheKey(id),
            e => base.FirstOrDefaultAsync(id)
        );
    }

    public override List<TEntity> GetAllList()
    {
        var entities = GetAllEntitiesFromCache();
        if (entities == null)
        {
            entities = base.GetAllList();
            foreach (var entity in entities)
            {
                PutInCache(entity);
            }
        }

        return entities.ToList();
    }

    public override async Task<List<TEntity>> GetAllListAsync()
    {
        var entities = GetAllEntitiesFromCache();
        if (entities == null)
        {
            entities = await base.GetAllListAsync();
            foreach (var entity in entities)
            {
                PutInCache(entity);
            }
        }

        return entities.ToList();
    }

    public override TEntity Get(TPrimaryKey id)
    {
        return _memoryCache.GetOrCreate(
            GetCacheKey(id),
            e => base.Get(id)
        );
    }

    public override Task<TEntity> GetAsync(TPrimaryKey id)
    {
        return _memoryCache.GetOrCreate(
            GetCacheKey(id),
            e => base.GetAsync(id)
        );
    }

    public override TEntity Insert(TEntity entity)
    {
        return PutInCache(base.Insert(entity));
    }

    public override TPrimaryKey InsertAndGetId(TEntity entity)
    {
        return Insert(entity).Id;
    }

    public override Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity)
    {
        return Task.FromResult(InsertAndGetId(entity));
    }

    public override Task<TEntity> InsertAsync(TEntity entity)
    {
        return Task.FromResult(Insert(entity));
    }

    public override TEntity Update(TEntity entity)
    {
        return UpdateInCache(base.Update(entity));
    }

    public override TEntity Update(TPrimaryKey id, Action<TEntity> updateAction)
    {
        return UpdateInCache(base.Update(id, updateAction));
    }

    public override Task<TEntity> UpdateAsync(TEntity entity)
    {
        return Task.FromResult(Update(entity));
    }

    public override async Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction)
    {
        return UpdateInCache(await base.UpdateAsync(id, updateAction));
    }

    #region Private

    private TEntity PutInCache(TEntity entity)
    {
        return _memoryCache.Set(GetCacheKey(entity.Id), entity);
    }

    private void RemoveFromCache(TPrimaryKey id)
    {
        _memoryCache.Remove(GetCacheKey(id));
    }

    private TEntity UpdateInCache(TEntity entity)
    {
        RemoveFromCache(entity.Id);
        return PutInCache(entity);
    }



    private string GetCacheKey(TPrimaryKey id)
    {
        return typeof(TEntity).FullName + id;
    }

    private IList<TEntity> GetAllEntitiesFromCache()
    {
        return _memoryCache.Get<List<TEntity>>(typeof(TEntity));
    }
    #endregion
}

我的问题:

我有很多关于我的 CacheRepository 的问题要问,但我必须减少它才能发布我的帖子。

=> 在 UnitOfWork.SubmitChanges 之前的 Insert/Edit/Delete 上直接在 CacheRepository 中添加/编辑/删除是不是一件好事?

如果您对 CacheRepository 有任何建议或建议,请分享,我真的很想了解更多。

【问题讨论】:

  • 在不了解这样做的目的(也不想知道)的情况下,我只能建议您考虑使用 EF Core 内存数据库提供程序。

标签: caching asp.net-core repository-pattern unit-of-work


【解决方案1】:

通常在进程未完成时从缓存中删除某些内容可能会导致问题。尚不确定更改是否会生效,因为即使使用 orm,您的 dbms 也会为您做出决定。您最终将深入挖掘所有异常并读取内容,而您本可以等待进程完成,然后再从给定缓存中删除对象。

当我深入研究您的代码(非常干净,为此 +1)时,我想到的其他事情如下

  1. Generic-Repository-Pattern 是 anti-pattern。我也喜欢将它用于简单的场景,但这并不是必需的。
  2. 您的CacheRepository 现在不仅负责缓存,还负责对数据库的CRUD 操作。这违反了SRP
  3. CacheUnitOfWork 不仅是一个缓存和一个工作单元,它还是一个工厂、一个存储库以及已经实现的功能之上的另一层。同样,这违反了 SRP。
  4. IMemoryCache is part of a library the belongs to AspNetCore。您在自己的实现中使用它,而它已经包装了将内容放入并发字典的逻辑。您可以简单地在控制器中使用它,必要时在您的操作中添加/更新或删除项目。
  5. 工作单元模式in this scenario is not necessary。使用您的 db-context 时,您在使用 context.Set&lt;TEnitity&gt;()... 以及将所有内容包装在存储库中时已经拥有它。

抽象是好事,但有时也可能很糟糕。

我知道其中的困难,这就是为什么我阅读了很多关于这些问题的文章并找到了一个很好的解决方案。 CQRS,中介和类似 DDD 的服务。

我还建议您查看AutoFac for IoC and DI。你现在手动做的很多事情(比如创建类型的工厂),可以使用它的assembly scanning 自动完成。它甚至可以与.NET Core native IoC 集成。

希望它可以帮助您获得一些想法。

【讨论】:

  • 正是我正在寻找的那种评论!非常感谢。在接受此答案之前,我会花时间阅读并遵循您的建议,也许其他人也会添加他们的 cmets。我可能很快就会回来提出其他问题:)
  • 是的,我从存储库中的 CUD 中删除了放入缓存
  • 我喜欢仅将我的 UnitOfWork 发送到我的 Controller 构造函数以获取我的 GenericRepository 的事实。所以根据你的建议,我可以做 Constructor(DbContext ctx){ var repo = new GenericRepository(ctx.Set()) } 这样的事情?
  • 您仍然可以这样做,但您可以将您的类型注册为泛型并让您的 ioc 容器完成工作,而不是手动创建它。本机 IOC 将是 services.AddScoped(typeof(IGeneric), typeof(Generic));例如。
猜你喜欢
  • 2020-01-21
  • 2017-03-12
  • 2018-10-17
  • 1970-01-01
  • 2020-01-10
  • 2017-10-23
  • 2011-10-11
  • 2021-09-07
相关资源
最近更新 更多