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

ASP.NET Core中如何在Scoped类中缓存并使用数据库数据

问题场景

需要缓存Category表的数据并在用户请求时返回,最初实现了如下缓存服务:

public interface ICategoryCache
{
    IEnumerable<Category> GetCategories();
}
public class CategoryCache : ICategoryCache
{
    private IEnumerable<Category> _categories;
    public CategoryCache(ItBuildsDbContext context)
    {
        _categories = context.Category.ToList();
    }

    public IEnumerable<Category> GetCategories()
    {
        return _categories;
    }
}

尝试将ICategoryCache注册为Singleton时,因依赖的ItBuildsDbContext是Scoped服务,且使用缓存的类为Scoped(无法改为Singleton),触发错误:Cannot consume scoped service。自己实现了一个工厂方案,询问是否有更优解。

当前实现方案
public class CategoryCache
{
    private readonly IEnumerable<Category> _categories;
    private static CategoryCache? _categoryCache;

    private CategoryCache(ItBuildsDbContext context)
    {
        _categories = context.Category.ToList();
    }

    public static CategoryCache Create(ItBuildsDbContext context)
    {
        if(_categoryCache == null)
        {
            _categoryCache = new CategoryCache(context);
        }

        return _categoryCache;
    }

    public IEnumerable<Category> GetCategories()
    {
        return _categories!;
    }
}

public interface IFactoryCategoryCache
{
    CategoryCache Create();
}

public class FactoryCategoryCache : IFactoryCategoryCache
{
    private readonly ItBuildsDbContext _context;
    public FactoryCategoryCache(ItBuildsDbContext context)
    {
        _context = context;
    }

    public CategoryCache Create()
    {
        return CategoryCache.Create(_context);
    }
}

// 注册代码
service.AddScoped<IFactoryCategoryCache, FactoryCategoryCache>();
更优解决方案

方案1:利用DI容器的工厂委托创建Singleton缓存

无需手动管理静态单例,通过DI容器的工厂委托,在创建Singleton缓存时临时创建作用域获取Scoped的DbContext:

public interface ICategoryCache
{
    IEnumerable<Category> GetCategories();
}

public class CategoryCache : ICategoryCache
{
    private readonly IEnumerable<Category> _categories;

    // 直接接收缓存数据,避免构造函数依赖Scoped服务
    public CategoryCache(IEnumerable<Category> categories)
    {
        _categories = categories;
    }

    public IEnumerable<Category> GetCategories() => _categories;
}

注册时使用工厂委托:

services.AddSingleton<ICategoryCache>(sp =>
{
    // 创建临时作用域,获取Scoped的DbContext
    using var scope = sp.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<ItBuildsDbContext>();
    var categories = dbContext.Category.ToList();
    return new CategoryCache(categories);
});

优势:

  • 由DI容器管理Singleton生命周期,无需手动实现单例逻辑
  • 仅在第一次初始化缓存时创建临时作用域,避免Scoped服务与Singleton的生命周期冲突
  • 天然线程安全,DI容器的Singleton创建逻辑本身是线程安全的

方案2:结合IMemoryCache实现可配置的缓存

如果需要支持缓存过期、自动刷新等特性,可以使用IMemoryCache封装缓存逻辑:

public interface ICategoryCache
{
    IEnumerable<Category> GetCategories();
}

public class CategoryCache : ICategoryCache
{
    private readonly IMemoryCache _memoryCache;
    private readonly IServiceScopeFactory _scopeFactory;
    private const string CacheKey = "CategoryList";

    public CategoryCache(IMemoryCache memoryCache, IServiceScopeFactory scopeFactory)
    {
        _memoryCache = memoryCache;
        _scopeFactory = scopeFactory;
    }

    public IEnumerable<Category> GetCategories()
    {
        // GetOrCreate会在缓存不存在时执行初始化逻辑
        return _memoryCache.GetOrCreate(CacheKey, entry =>
        {
            // 可选:设置缓存过期时间,比如1小时自动刷新
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
            
            using var scope = _scopeFactory.CreateScope();
            var dbContext = scope.ServiceProvider.GetRequiredService<ItBuildsDbContext>();
            return dbContext.Category.ToList();
        });
    }
}

注册代码:

services.AddMemoryCache(); // 注册内存缓存服务
services.AddSingleton<ICategoryCache, CategoryCache>();

优势:

  • 支持灵活的缓存过期策略
  • 无需手动管理单例,DI容器负责生命周期
  • 缓存逻辑更通用,可扩展支持缓存依赖、滑动过期等特性

方案对比

你当前的实现存在线程安全隐患:静态Create方法未加锁,多线程并发调用时可能创建多个CategoryCache实例。上述两种方案均解决了该问题,且更符合.NET依赖注入的设计原则,代码更简洁易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 12:55:25