Я создал пользовательскую реализацию IFileProvider
.Класс зависит от другого объекта (который настроен как одноэлементный), но у меня есть несколько проблем при его настройке.Изначально я начал с написания метода, который выглядит следующим образом:
public static IServiceCollection SetupPluginSystem(this IServiceCollection services) {
try {
//register related services before initializing
// new plugin model
services.AddSingleton<IPluginInstanceCreator, PluginInstanceCreator>();
services.AddSingleton<IAuthPluginProviderManager>(sp => new AuthPluginProviderManager(
sp.GetService<IPluginInstanceCreator>(),
sp.GetService<ILogger<AuthPluginProviderManager>>()
));
var provider = services.BuildServiceProvider();
//plugins loaded during 1st instantication (singleton)
//now we're setting up the view embeded file provider with required info
var pluginManager = provider.GetService<IAuthPluginProviderManager>();
var logService = provider.GetService<ILogService>();
var cnn = provider.GetService<IDbConnection>();
LoadPluginsFromDb(pluginManager, cnn, logService);
services.Configure<RazorViewEngineOptions>(x => x.FileProviders.Add(new EmbeddedPluginFileProvider(pluginManager)));
}
catch (Exception ex) {
var provider = services.BuildServiceProvider();
var logService = provider.GetService<ILogService>();
logService.LogException(ex, new List<KeyValuePair<string, string>> {new KeyValuePair<string, string>("Method Name", "LoadAuthPlugins")});
}
return services;
}
Как вы можете видеть, идея состояла в том, чтобы инициализировать менеджер плагинов информацией из базы данных.И нет, это не сработало, потому что во время выполнения у меня было 2 экземпляра IAuthPluginProviderManager
(один был построен из контейнера, созданного моим вызовом BuildServiceProvider
, а другой - из контейнера, сгенерированного ASP.Инфраструктура NET Core).
Итак, я подумал о переносе кода инициализации в лямбда-код, который регистрирует AuthPluginProviderManager
в качестве одиночного:
public static IServiceCollection SetupPluginSystem(this IServiceCollection services) {
try {
//register related services before initializing
// new plugin model
services.AddSingleton<IPluginInstanceCreator, PluginInstanceCreator>();
services.AddSingleton<IAuthPluginProviderManager>(sp => {
var pluginManager = new AuthPluginProviderManager(
sp.GetService<IPluginInstanceCreator>(),
sp.GetService<ILogger<AuthPluginProviderManager>>());
var logService = sp.GetService<ILogService>();
var cnn = sp.GetService<IDbConnection>();
LoadPluginsFromDb(pluginManager, cnn, logService);
});
// but now how do I register my IFileProvider?
services.Configure<RazorViewEngineOptions>(x => x.FileProviders.Add(new EmbeddedPluginFileProvider(pluginManager)));
}
catch (Exception ex) { ... }
return services;
}
К сожалению, существует проблема передачиМенеджер плагинов синглтон для конструктора моего пользовательского IFileProvider
... В отличие от методов AddXXX, используемых для регистрации сервисов, нет перегрузки метода Configure
, который принимает Func
для разрешения экземпляра во время выполнения.
В конце концов, я скомпрометировал что-то вроде этого:
public static IServiceCollection SetupPluginSystem(this IServiceCollection services) {
try {
services.AddSingleton<IPluginInstanceCreator, PluginInstanceCreator>();
var provider = services.BuildServiceProvider();
var pluginManager = new AuthPluginProviderManager(
provider.GetService<IPluginInstanceCreator>(),
provider.GetService<ILogger<AuthPluginProviderManager>>()
);
var logService = provider.GetService<ILogService>();
var cnn = provider.GetService<IDbConnection>();
LoadPluginsFromDb(pluginManager, cnn, logService);
services.AddSingleton<IAuthPluginProviderManager>(sp => pluginManager);
services.Configure<RazorViewEngineOptions>(x => x.FileProviders.Add(new EmbeddedPluginFileProvider(pluginManager)));
}
catch (Exception ex) {...}
return services;
}
С помощью этого решения я создаю экземпляр объекта, который должен использоваться всякий раз, когда кто-то проситIAuthPluginProviderManager
.И да, это всегда будет возвращать один и тот же объект, даже если вы вызываете BuildServiceProvider
несколько раз.
Несмотря на то, что это работает, мне было интересно, есть ли другое более элегантное решение для этих сценариев.Я имею в виду, это единственный способ создать «настоящий» синглтон при использовании DI в ASP.NET Core?Есть ли лучшее решение для этих типов сценариев?
Спасибо.