Создание одноэлементного CacheManager в Asp.Net Сердечник

#asp.net-core #asp.net-core-mvc #asp.net-core-5.0 #memorycache

Вопрос:

Я пытаюсь создать Singleton класс CacheManager, от которого зависит IMemoryCache .

 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, который также (по умолчанию) зарегистрирован в Scoped lifetime. Я не хочу менять срок службы этих сервисов.

Однако я хочу , чтобы 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, если она становится нулевой?

Комментарии:

1. работает ли вообще ваша последняя регистрация кода блока ? AFAIK, он просто создаст одноэлементный экземпляр CacheManager из отдельной области, вернет отдельный экземпляр DbContext , но DbContext теперь он тоже станет одноэлементным. Что должно привести к различным проблемам с процессом определения области и отслеживанием изменений, пока вы продолжаете его использовать (лично я думаю, что через некоторое время после развертывания он должен выдавать множество исключений)

2. зачем ему возвращать одноэлементный DbContext? scope.ServiceProvider.GetService<ILookupService>() строка должна возвращать экземпляр с областью действия. По умолчанию в веб-приложении EF зарегистрирует экземпляр DbContext с областью действия

3. Заводской метод вызывается только один раз при инициализации CacheManager экземпляра, поэтому lookupService CacheManager для первой загрузки данных был создан только один экземпляр. Тогда, в любом случае, в чем здесь разница ? Или если ваше намерение с самого начала состояло в том, чтобы просто использовать lookupService исходные данные CacheManager , тогда все должно быть в порядке. Меня, однако CacheManager , использовали ILookupService в качестве зависимости. Я могу что-то упустить-уже кое-что понял

4. Да, служба поиска используется для загрузки данных в кэш из базы данных при запуске приложения. Мы не можем внедрить ILookupService в качестве зависимости в CacheManager. Платформа DI выдаст ошибку as Singleton Cannot consume scoped service . Я хочу знать, является ли CacheManager одноэлементным, как правильно повторно заполнить CacheEntry, когда он становится нулевым

5. Обычный способ службы с более длительным сроком службы для доступа к службе с более коротким сроком службы через DI-это внедрить фабрику, которая создает службы с более коротким сроком службы по мере необходимости. Разве это невозможно в вашем случае?

Ответ №1:

Я думаю, что я бы просто настроил службы.AddSingleton<CacheManager>(); (CacheManager, имеющий конструктор по умолчанию)

После настройки всех зависимостей DI и наличия поставщика услуг, получите синглтон Cachemanager и инициализируйте его с помощью LoadCache. (поэтому позвольте DI создать «пустой» одноэлементный cachemanager, но немедленно инициализируйте его где-нибудь при запуске приложения)

 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

.Net использует контейнер для каждого шаблона запроса, в котором объекты с областью действия хранятся в объекте HttpRequest. Таким образом, синглтону в основном нужно запросить текущий сервис ILookup, что делается путем вызова:

 container.GetService<ILookupService>
 

Поэтому включите контейнер DI в качестве аргумента конструктора в свой класс CacheManager, и все будет готово. Это шаблон локатора служб, который необходим для удовлетворения ваших требований.

Альтернативным механизмом разрешения запросов является использование объекта 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.Defau<
        }
}