Добавление нового IFileProvider с зависимостью: заставить его работать с DI ядра asp.net - PullRequest
0 голосов
/ 28 января 2019

Я создал пользовательскую реализацию 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?Есть ли лучшее решение для этих типов сценариев?

Спасибо.

...