【问题标题】:Creating Singleton CacheManager in Asp.Net Core在 Asp.Net Core 中创建 Singleton CacheManager
【发布时间】:2021-09-14 16:13:20
【问题描述】:

我正在尝试创建依赖于IMemoryCacheSingleton CacheManager 类。

public class CacheManager:ICacheManager
{
   private readonly IMemoryCache _cache;
   
   public CacheManager(IMemoryCache cache)
   {
     _cache = cache;
   }
   
   public void LoadCache(MyData data)
   {
        // load cache here at startup from DB
   }

}

我还有一个Scoped 服务,可以从数据库中检索数据

public class LookupService:ILookupService
{
   private readonly MyDatabaseContext _dbContext;
   
   public class LookupService(MyDatabaseContext dbContext)
   {
      _dbContext = dbContext;
   }
  
  public void Dispose()
  {
     //Dispose DBContext here
  }
  
   // some async methods that returns lookup collection    
}

在 Startup.cs 中注册这些服务

    public void ConfigureServices(IServiceCollection services)
    {
        // EF
        services.AddDbContext<MyDatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        // domain services            
        services.AddScoped<ILookupService, LookupService>();
        
        services.AddMemoryCache();
        
        // singleton
        services.AddSingleton<CacheManager>(sp=> 
        { 
            using(var scope = sp.CreateScope())
            {
                using (var service = scope.ServiceProvider.GetService<ILookupService>())
                {
                    how do i create cacheManager instance by injecting IMemoryCache and also register callback function
                }
            }
        });                 
    }
    

ILookupService 注册为Scoped 服务,因为它依赖于 DBContext,DBContext 也(默认情况下)注册Scoped 生命周期。我不想更改这些服务的生命周期。

但是我希望将 CacheManager 注册为 Singleton,这意味着我不能将 ILookupService 作为依赖项注入到 CacheManager 中。

所以这是我创建和注册 CacheManager 单例实例的可能解决方案

        services.AddSingleton<CacheManager>(sp=> 
        { 
            using(var scope = sp.CreateScope())
            {
                using (var lookupService = scope.ServiceProvider.GetService<ILookupService>())
                {
                    var cache = scope.ServiceProvider.GetService<IMemoryCache>();
                    var manger =  new CacheManager(cache);
                    manger.LoadCache(lookupService.GetData());
                    return manger;
                }
            }
        });   
        

不确定这是创建 CacheManager 的最佳方式。如果 CacheEntry 变为 null,如何实现回调函数重新填充?

【问题讨论】:

  • 你的最后一个块码寄存器还能用吗? AFAIK,它只是从单独的范围创建CacheManager 的单例实例,将返回DbContext 的独立实例,但DbContext 现在也将成为单例。只要您继续使用它,这应该会导致范围过程和更改跟踪的各种问题(我个人认为它应该在部署后一段时间后抛出许多异常)
  • 为什么会返回单例 DBContext? scope.ServiceProvider.GetService&lt;ILookupService&gt;() 行应该返回作用域实例。默认情况下,在 Web App 中,EF 将注册 DBContext 的作用域实例
  • 工厂方法在CacheManager实例初始化时只调用了一次,因此只创建了一个lookupService实例来帮助CacheManager第一次加载数据。那么这里有什么区别呢?或者,如果您从一开始就打算将lookupService 用于CacheManager 的初始数据,那么它应该没问题。我虽然 CacheManager 使用 ILookupService 作为依赖项。我可能已经错过了一些东西
  • 是的,查找服务用于在应用程序启动时将数据从数据库加载到缓存中。我们不能在 CacheManager 中注入 ILookupService 作为依赖项。 DI 框架将抛出​​错误为Singleton Cannot consume scoped service。我想知道CacheManager是否是Singleton,当它变为null时重新填充CacheEntry的正确方法是什么
  • 长寿命服务通过 DI 访问短寿命服务的常用方法是注入一个根据需要创建短寿命服务的工厂。在你的情况下这是不可能的吗?

标签: asp.net-core asp.net-core-mvc asp.net-core-5.0 memorycache


【解决方案1】:

我想我会简单地配置 services.AddSingleton(); (CacheManager 有一个默认构造函数)

在配置完所有的 DI 依赖并拥有一个 serviceprovider 之后,获取 Cachemanager 单例并使用 LoadCache 对其进行初始化。 (所以让 DI 创建“空”单例缓存管理器,但在应用程序启动时立即初始化)

var cachemanager = scope.ServiceProvider.Get<CacheManager>();
var lookupService = scope.ServiceProvider.Get<ILookupService>();
var cache = scope.ServiceProvider.Get<IMemoryCache>();
cachemanager.Cache = cache;
cachemanager.LoadCache(lookupService.GetData());

【讨论】:

    【解决方案2】:

    看起来根本问题是 ILookupService 在运行时和请求开始进入之前无法解决。您需要在此之前创建 CacheManager。

    DI 构图

    这应该在应用程序启动时完成 - 如this class of mine。注意不同类型对象的不同生命周期,但我只关注创建对象而不是交互。

    DI 分辨率

    .Net 使用每个请求模式的容器,其中针对 HttpRequest 对象存储作用域对象。所以单例基本上需要查询当前的ILookupService,通过调用来完成:

    container.GetService<ILookupService>
    

    因此,将 DI 容器作为构造函数参数包含在 CacheManager 类中,您将全部设置完毕。这是服务定位器模式,需要满足您的要求。

    另一种按请求解析机制是通过 this class 中的 HttpContext 对象,其中使用了以下代码:

    IAuthorizer authorizer = (IAuthorizer)this.Context.RequestServices.GetService(typeof(IAuthorizer));
    

    摘要

    重要的是理解上面的设计模式,然后你就可以将它应用到任何技术中。

    【讨论】:

      【解决方案3】:

      将缓存服务注册为单例,试试下面的代码

       public class CacheService : ICacheService
          {
              private ObjectCache _memoryCache;
      
              /// <summary>
              /// Initializes a new instance of the <see cref="CacheService"/> class.
              /// </summary>
              public CacheService()
              {
                  this._memoryCache = System.Runtime.Caching.MemoryCache.Default;
              }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-15
        • 1970-01-01
        • 2019-05-10
        • 1970-01-01
        • 2016-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多