使缓存的数据和依赖注入模式无效

时间:2019-09-26 16:06:42

标签: asp.net-core dependency-injection .net-core simple-injector

我有一个数据缓存类(使用MemoryCache类)。

此类的基本功能是缓存参考数据。为了获得此参考数据,它需要一个Entity Framework dbContext实例。这通过依赖项注入(简单注入器)传递。

但是此dbContext的生命周期为“每次调用”(AsyncScopedLifestyle)。因此,为了满足这一要求,我将调用设置为在“作用域”中设置缓存,该作用域在调用后到期。

缓存每2小时失效一次,然后重新查询。毫不奇怪,那时dbContext已被清理(因为它超出了范围)。

我可以想办法解决这个问题。但是我想知道我是否应该针对这种问题遵循某种模式。 (我的大多数解决方案都是让我将容器传递到缓存类中。但这似乎违反了几种DI模式。)

是否有人知道在班级内部需要再次注入时要使用的设计模式?

更多背景信息:

  • 我的缓存类(称为DataCache)从构造函数注入中获取上下文。
  • 通过Startup.cs中的Configure方法进行设置。看起来像这样:

using (AsyncScopedLifestyle.BeginScope(container))
{
    // Setup the long lived data caching
    var dataCache = container.GetInstance<DataCache>();
    dataCache.SetupCachedItems();
}
  • 它设置MemoryCache使两个小时后的缓存中的数据过期。但是到那时,注入的上下文早已清除。

2 个答案:

答案 0 :(得分:2)

我在这里看到两个通用的解决方案:

  1. DataCache管理的缓存移出该类,以使MyCacheClass可以成为Scoped。毫无疑问,这很可能是MemoryCache的目的。内存缓存可能是单例。
  2. DataCache移到Composition Root中,以便它可以安全地依赖容器(或容器抽象),而不会陷入Service Locator anti-pattern陷阱中。

第一种解决方案可以多种方式应用。也许是在static字段中定义缓存的问题:

public class DataCache
{
    private static ConcurrentDictionary<string, object> cache;
}

如果您将MemoryCache作为数据的存储提供者注入,它将包含缓存,而DataCache的生活方式将变得无关紧要:

public class DataCache
{
    public DataCache(MyContext context, IMemoryCache cache)
}

但是,如果需要将DataCache注入Singleton使用者,则它本身必须是Singleton。由于MyContext需要确定范围,以防止Captive Dependencies,因此无法使用此方法。为此,您可以使用解决方案2。

使用解决方案,您可以确保在Composition Root内部创建DataCache。这迫使您将DataCache隐藏在抽象后面,例如IDataCache。可以将这种抽象放置在允许消费者依赖的位置,而DataCache实现将完全隐藏在“合成根”内部。在那个位置依靠DI容器变得安全。

// Part of the Composition Root
sealed class DataCache: IDataCache
{
    public DataCache(Container container, IMemoryCache cache) ...

    public ProductData GetProductByKey(string key)
    {
        if (key not in cache)
        {
            using (AsyncScopedLifestyle.BeginScope(this.container))
            {
                var context = container.GetInstance<MyContext>();
                var p = context.Products.SingleOrDefault(p => p.Key == key);
                var data = new ProductData(p);
                AddProductToCache(key, data);
                return data;
            }
        }
    }
}

答案 1 :(得分:-1)

您应该完全依赖DI。换句话说,如果缓存类需要上下文,则这是一个依赖项,应这样注入:

public class MyCacheClass
{
    private readonly MyContext _context;

    public MyCacheClass(MyContext context)
    {
        _context = context;
    }

    ...
}

这当然假定缓存类也具有作用域生存期,这实际上没有理由不应该,因为它与作用域依赖项进行交互。但是,如果由于某种原因您需要使其具有单身有效期,则可以简单地注入IServiceProvider,然后创建作用域并在需要时拉出上下文:

using (var scope = _serviceProvider.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<MyContext>();
    // do something with context
}

如果您使用的是静态类,请不要。