You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型仓储结合装饰器模式实现缓存的最佳实践咨询

泛型仓储+缓存的最佳实践(针对特定方法缓存)

核心思路

不用纠结VS自动生成的所有接口方法,大部分方法直接委托给原始仓储实现即可,只对需要缓存的方法做定制处理——这才是装饰器模式的核心:只增强需要的功能,其余逻辑完全复用原有实现。

具体实现步骤

  1. 编写CachedBlogRepository装饰器
    实现IBlogRepository,注入原始BlogRepository和IMemoryCache。不需要缓存的方法直接调用原始仓储的对应逻辑;仅在GetBlogsByCreatorAsync中添加缓存逻辑。

    示例代码:

    public class CachedBlogRepository : IBlogRepository
    {
        private readonly IBlogRepository _originalRepo;
        private readonly IMemoryCache _cache;
        private const string _cacheKeyPrefix = "Blogs_Creator_";
    
        public CachedBlogRepository(IBlogRepository originalRepo, IMemoryCache cache)
        {
            _originalRepo = originalRepo;
            _cache = cache;
        }
    
        // 无需缓存的方法,直接转发给原始仓储
        public Task<Blog> GetBlogByIdAsync(int id)
        {
            return _originalRepo.GetBlogByIdAsync(id);
        }
    
        public async Task AddBlogAsync(Blog blog)
        {
            // 关键:增删改操作要清理对应缓存,避免数据不一致
            _cache.Remove(_cacheKeyPrefix + blog.CreatorId);
            await _originalRepo.AddBlogAsync(blog);
        }
    
        // 仅对该方法做缓存处理
        public async Task<IEnumerable<Blog>> GetBlogsByCreatorAsync(int creatorId)
        {
            var cacheKey = _cacheKeyPrefix + creatorId;
            return await _cache.GetOrCreateAsync(cacheKey, async entry =>
            {
                // 设置缓存过期策略,比如10分钟自动失效
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
                // 缓存不存在时,从原始仓储获取数据
                return await _originalRepo.GetBlogsByCreatorAsync(creatorId);
            });
        }
    
        // 其他自动生成的接口方法,全部直接转发给_originalRepo即可
        // 比如:public Task UpdateBlogAsync(Blog blog) => _originalRepo.UpdateBlogAsync(blog);
        // ...
    }
    
  2. 依赖注入配置
    在DI容器中先注册原始仓储,再用装饰器包装它,确保上层调用拿到的是带缓存的实例:

    // 注册原始仓储
    services.AddScoped<IBlogRepository, BlogRepository>();
    // 用装饰器包装(部分DI框架自带Decorate方法,比如Microsoft.Extensions.DependencyInjection)
    services.Decorate<IBlogRepository, CachedBlogRepository>();
    
    // 如果没有Decorate方法,手动注册方式:
    // services.AddScoped<IBlogRepository>(sp => 
    //     new CachedBlogRepository(sp.GetRequiredService<BlogRepository>(), sp.GetRequiredService<IMemoryCache>()));
    
  3. 缓存一致性维护
    执行博客的增、删、改操作时,必须清理对应作者的缓存(比如示例中AddBlogAsync里的缓存移除操作),防止缓存数据与数据库不一致。

为什么这是最佳实践?

  • 符合单一职责原则:原始仓储只负责数据访问,装饰器只专注缓存增强,职责完全分离。
  • 对上层透明:业务代码无需修改,依然依赖IBlogRepository,完全感知不到缓存的存在。
  • 扩展性强:后续要给其他方法加缓存,只需在装饰器中修改对应方法,不用动原始仓储代码。

内容的提问来源于stack exchange,提问作者totesz09

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 16:10:23