Как объединить несколько массивов из файлов конфигурации ASP.Net Core? - PullRequest
0 голосов
/ 31 декабря 2018

Я хотел бы динамически загружать и регистрировать сервисы в моем приложении.Для этого мне нужно иметь возможность загружать файлы конфигурации из разных проектов в решении и объединять значения из них в один массив json.К сожалению, по умолчанию в конфигурации ASP.Net Core значения переопределяются.

Я регистрирую файлы со следующим кодом (часть файла Program.cs):

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((webHostBuilderContext, configurationbuilder) =>
            {
                var env = webHostBuilderContext.HostingEnvironment;
                configurationbuilder.SetBasePath(env.ContentRootPath);
                configurationbuilder.AddJsonFile("appsettings.json", false, true);

                var path = Path.Combine(env.ContentRootPath, "App_Config\\Include");

                foreach(var file in Directory.EnumerateFiles(path, "*.json",SearchOption.AllDirectories))
                {
                    configurationbuilder.AddJsonFile(file, false, true);
                }
                configurationbuilder.AddEnvironmentVariables();
            })
            .UseStartup<Startup>();

Код ищет все файлы с расширением *.json в каталоге App_Config\Include и добавляет всеих к конструктору конфигурации.

Структура файлов выглядит следующим образом:

{
  "ServicesConfiguration": {
    "Services": [
      {
        "AssemblyName": "ParsingEngine.ServicesConfigurator, ParsingEngine"
      }
    ]
  }
}

Как видите, у меня есть основной раздел ServicesConfiguration затем Services массив с объектами, которые имеют один атрибут AssemblyName.

Чтобы прочитать эти значения, я использую ServicesConfiguration класс со списком:

public class ServicesConfiguration
    {
        public List<ServiceAssembly> Services { get; set; }
    }

И этот список использует ServiceAssembly класс:

public class ServiceAssembly
    {
        public string AssemblyName { get; set; }
    }

Для загрузки этой конфигурацииЯ использую IOptions на уровне конструктора (DI):

Microsoft.Extensions.Options.IOptions<ServicesConfiguration> servicesConfiguration,

И кажется, что конфигурация загружена - но значения из файлов не объединяются, а переопределяются последним найденным файлом.

Любые идеикак это исправить?

1 Ответ

0 голосов
/ 31 декабря 2018

Итак, у вас есть представление о том, что я имел в виду в своих комментариях. Вот потенциальный ответ

Поскольку вам приходится загружать разные «конфигурационные» файлы из разных проектов и применять к ним некоторую логику слияния, я бы просто избегалиспользуя систему конфигурации «по умолчанию» для загрузки файлов JSON в приложение.Вместо этого я бы сделал это сам.Итак:

  1. Считайте и десериализуйте JSON в тип и сохраните его в списке
  2. Просмотрите список, содержащий все конфиги, и примените свою логику объединения
  3. Регистрацияодин ServicesConfiguration как Singleton
  4. Удалите код, который был у вас на Program.cs для загрузки пользовательских JSON файлов

Вот как вы можете это сделать:

ServicesRootConfiguration (новый класс, чтобы можно было десериализовать JSON)

public class ServicesRootConfiguration
{
    public ServicesConfiguration ServicesConfiguration { get; set; }
}

Startup.cs

public class Startup
{
    private readonly IHostingEnvironment _hostingEnvironment;

    public Startup(IConfiguration configuration, IHostingEnvironment env)
    {
        Configuration = configuration;
        _hostingEnvironment = env;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        // other configuration omitted for brevity

        // build your custom configuration from json files
        var myCustomConfig = BuildCustomConfiguration(_hostingEnvironment);

        // Register the configuration as a Singleton
        services.AddSingleton(myCustomConfig);
    }

    private static ServicesConfiguration BuildCustomConfiguration(IHostingEnvironment env)
    {
        var allConfigs = new List<ServicesRootConfiguration>();

        var path = Path.Combine(env.ContentRootPath, "App_Config");

        foreach (var file in Directory.EnumerateFiles(path, "*.json", SearchOption.AllDirectories))
        {
            var config = JsonConvert.DeserializeObject<ServicesRootConfiguration>(File.ReadAllText(file));
            allConfigs.Add(config);
        }

        // do your logic to "merge" the each config into a single ServicesConfiguration
        // here I simply select the AssemblyName from all files.
        var mergedConfig = new ServicesConfiguration
        {
            Services = allConfigs.SelectMany(c => c.ServicesConfiguration.Services).ToList()
        };

        return mergedConfig;
    }
}

Тогда в вашем Controller просто нормальнополучить экземпляр по DI.

public class HomeController : Controller
{
    private readonly ServicesConfiguration _config;

    public HomeController(ServicesConfiguration config)
    {
        _config = config ?? throw new ArgumentNullException(nameof(config));
    }
}

При таком подходе вы получили то же поведение, что и при обычной регистрации IOptions.Но вы избегаете зависимости от него и необходимости использовать уродливый .Value (ург).Более того, вы можете зарегистрировать его как интерфейс, чтобы он облегчал вашу жизнь во время тестирования / проверки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...