Asp.net core 使缓存数据和依赖项注入模式无效
我有一个数据缓存类(使用Asp.net core 使缓存数据和依赖项注入模式无效,asp.net-core,dependency-injection,.net-core,simple-injector,Asp.net Core,Dependency Injection,.net Core,Simple Injector,我有一个数据缓存类(使用MemoryCache类) 此类的基本功能是缓存引用数据。要获取此引用数据,它需要实体框架dbContext的实例。这是通过依赖项注入(简单注入器)传入的 但是这个dbContext的生命周期是“每次调用”(AsyncScopedLifestyle)。因此,为了满足这一点,我将设置缓存的调用放在一个“作用域”中,该“作用域”在调用后过期 缓存每2小时失效一次,并重新查询。毫不奇怪,dbContext到那时已经清理完毕(因为它超出了范围) 我可以想办法绕过这个问题。但我想知
MemoryCache
类)
此类的基本功能是缓存引用数据。要获取此引用数据,它需要实体框架dbContext的实例。这是通过依赖项注入(简单注入器)传入的
但是这个dbContext的生命周期是“每次调用”(AsyncScopedLifestyle
)。因此,为了满足这一点,我将设置缓存的调用放在一个“作用域”中,该“作用域”在调用后过期
缓存每2小时失效一次,并重新查询。毫不奇怪,dbContext到那时已经清理完毕(因为它超出了范围)
我可以想办法绕过这个问题。但我想知道,对于这类问题,我是否应该遵循一种模式。(我的大多数解决方案都让我将容器传递到缓存类中。但这似乎违反了几个DI模式。)
有人知道在类内部重复需要注入时要使用的设计模式吗?
更多的背景:
- 我的缓存类(称为
)从构造函数注入中获取上下文DataCache
- 通过Startup.cs中的Configure方法进行设置调用。这看起来像这样:
使用(AsyncScopedLifestyle.BeginScope(容器))
{
//设置长期数据缓存
var dataCache=container.GetInstance();
SetupCachedItems();
}
- 它将MemoryCache设置为在两小时后使缓存中的数据过期。但到那时,注入的上下文早已清理干净
- 您应该全程依赖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
}
使用(var scope=\u serviceProvider.CreateScope())
{
var context=scope.ServiceProvider.GetRequiredService();
//根据上下文做一些事情
}
如果您使用的是静态类,请不要这样做。我在这里看到两种通用解决方案:
DataCache
管理的缓存移出该类,这样MyCacheClass
可以成为范围。这很容易理解,因为这可能就是MemoryCache
的用途。内存缓存很可能是单例的
DataCache
移动到中,这样它就可以安全地依赖于容器(或容器抽象),而不会落入陷阱静态
字段中定义缓存的问题:
公共类数据缓存
{
私有静态ConcurrentDictionary缓存;
}
如果您将MemoryCache
作为数据的存储提供程序注入,它将包含缓存,DataCache
的生活方式将变得无关紧要:
公共类数据缓存
{
公共数据缓存(MyContext上下文,IMemoryCache缓存)
}
但是,如果需要将DataCache
注入到单例消费者中,那么它本身就需要是单例的。这不允许使用这种方法,因为需要对MyContext的范围进行限定,以防止出现错误。为此,您可以使用解决方案2
使用解决方案,您可以确保在您的内部创建数据缓存。这迫使您将DataCache
隐藏在抽象后面,例如IDataCache
。这个抽象可以放在一个允许使用者依赖的位置,而DataCache
实现将完全隐藏在合成根中。在该位置,依赖DI容器变得安全
//组成根目录的一部分
密封类数据缓存:IDataCache
{
公共数据缓存(容器容器,IMemoryCache缓存)。。。
public ProductData GetProductByKey(字符串键)
{
如果(密钥不在缓存中)
{
使用(AsyncScopedLifestyle.BeginScope(this.container))
{
var context=container.GetInstance();
var p=context.Products.SingleOrDefault(p=>p.Key==Key);
var数据=新产品数据(p);
AddProductToCache(键、数据);
返回数据;
}
}
}
}
数据缓存类是什么样子的?它从何处获得上下文以及如何获得上下文?在任何情况下,它都应该仅在需要加载新数据时请求上下文。当这种情况发生时,它需要创建一个作用域来获取每次调用的上下文。问题与此类似。能否使用通过在后台定期刷新缓存的AddHostedService
设置的BackgroundService
?DBContext可以被注入到后台服务中
,因此在单个请求的范围之外可用。尽管答案写得很好,但很抱歉对您投了否决票。我真的不喜欢这里使用服务定位器。服务定位器是反模式的,因为总有更好的解决方案。尽管asp.net内核被标记了,但这个问题与IServiceProvider并没有真正的关系。这不是反对票的目的。而且,当你有一个单例类,这是你唯一的选择。没有更好的选择了。正如我在回答中所说的,最好使用作用域生存期。也是这样,因为无论您使用什么DI容器,它仍然使用Microsoft.Extensions.DependencyInjection facades。我的缓存在单实例生存期内运行。因为它只是挂在周围试图阻止较慢的呼叫。如果它被重新创建
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyContext>();
// do something with context
}