#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<
}
}