【问题标题】:How to resolve scoped service inside singleton object如何在单例对象中解析范围服务
【发布时间】:2020-08-27 12:59:00
【问题描述】:

我有MemoryCache 对象(应用程序、配置等),我将它们注册为Singleton。还有一些作用域存储库,可以从 db 中选择数据来填充缓存。

例如这里是Singleton注册的类,

public class ApplicationCache : MultipleLoadCache<Application>
{
    public ApplicationCache() 
    {
            
    }
}

MultipleLoadCache 覆盖CacheItemPolicy,(还有SingleLoadCache),

public class MultipleLoadCache<TEntity> : SmartCache<TEntity> where TEntity : class
{
    public MultipleLoadCache()
    {
    }

    protected override CacheItemPolicy SetPolicy()
    {
        return new CacheItemPolicy()
        {
           AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(15)
        };
    }
 }

基类是,

public class SmartCache<TEntity> : IDisposable where TEntity : class
{
      public bool TryGetList(IRepository<TEntity> repository, out List<TEntity> valueList)
      {
          valueList = null;
          lock (cacheLock)
          {
              GenerateCacheIfNotExists(repository, out valueList);
              if (valueList == null || valueList.Count == 0)
              {
                valueList = (List<TEntity>)_memoryCache.Get(key);
              }
          }

          return valueList != null;
      }

我知道作用域服务不能注入单例类。所以我更喜欢使用方法注入。

private void GenerateCacheIfNotExists(IRepository<TEntity> repository, out List<TEntity> list)
{
          list = null;
          if (!_memoryCache.Any(x => x.Key == key)) // if key not exists, get db records from repo.
          {
              IEnumerable<TEntity> tempList = repository.GetList();
              list = tempList.ToList();
              _cacheItemPolicy = SetPolicy();
              SetCacheList(list);
          }
      }
}

在控制器上,我尝试获取缓存值,但这部分对我来说似乎是错误的。如果我尝试获取缓存值,我不应该将存储库作为参数传递。

private readonly ApplicationCache _appCache;
public LogController(ApplicationCache appCache)
{
   _appCache = appCache;
}

[HttpPost]
[Route("Register")]
public List<Application> Register([FromServices] IApplicationRepository repository)
{
   List<Application> cf;
   _appCache.TryGetList(repository, out cf);

   return cf;
}

另外,通过方法注入。我也无法使用CacheItemPolicyRemovedCallBack 事件。因为,当回调触发(重新加载缓存)时,我需要存储库再次从数据库中获取记录。

这个设计看起来不错,使用MemoryCache的回调事件来做到这一点的最佳设计是什么?

更新 1-

public void ConfigureServices(IServiceCollection services)
{
     services.AddControllers();
     services.AddMemoryCache();
     services.AddSingleton(x => new ApplicationCache());
     services.AddScoped<IApplicationRepository, ApplicationRepository>();
}

谢谢,

【问题讨论】:

  • 你应该有一个 DataRepository 类,它有两个依赖项 1 - 缓存层和 2- 数据访问层.. 这样你就可以按照你想要的方式注入这两个依赖项......
  • 你能展示你的Startup ConfigureServices 例程,你在哪里注入所有东西吗?
  • @ChetanRanpariya 这个问题与“存储库”anti模式无关。它专门用于使用范围服务,.NET Core 文档中已经记录了一些内容
  • 在单例中使用作用域服务是托管服务的常见问题,因此在 Consuming a scoped service in a Background task 的后台服务文章中对此进行了记录。您将IServiceProvider 传递给您的单例并显式使用CreateScope 创建一个范围,然后使用该范围对象创建例如DbContext 实例
  • 请检查更新@Andy

标签: c# .net-core memorycache


【解决方案1】:

我遇到了同样的问题。由于静态类是在开始时编译的,因此以后无法注入所需的服务。我通过使用 IServiceScopeFactory 弄清楚了。

你基本上是在构造函数中注入 IServiceScopeFactory serviceScopeFactory 。

static SampleClass(IServiceScopeFactory serviceScopeFactory){  
   //serviceScopedFactory will act as Singleton, since it is a static class
   _serviceScopeFactory = serviceScopeFactory;
}

并在方法中这样使用:

using (var scope = _serviceScopeFactory.CreateScope())
{
  var service = scope.ServiceProvider.GetRequiredService<IService>();
  //Here you can use the service. This will be used as Scoped since it will be 
  //recreated everytime it is called
   
}

【讨论】:

  • 那么,在单例类中注入 IServiceScopeFactory 也不会使工厂单例?还是已经单例注册了?
  • serviceScopedFactory 将是单例,因为该类是静态的
  • 但是使用GetRequiredService创建的服务会在每次调用该方法时创建。所以它与作用域相同。
  • 你可能错过了静态类不能有带参数的构造函数的地方。
  • 是的,我完全错过了这一点,因为我在托管服务中使用它。有这个链接Resolving Instances With .Net Core DI in Static Classes你应该看看这个
猜你喜欢
  • 2020-08-05
  • 1970-01-01
  • 2019-06-04
  • 2018-09-27
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多