Перейти к содержанию

Расширения для обработки конфигурационных файлов

Обработка конфигурационных файлов app.json выполняется по алгоритму, описанному в разделе Файлы app.json. Результирующая конфигурация доступна через объект IConfigurationManager из DI-контейнера.

Ниже рассмотрены примеры по расширению алгоритма обработки конфигурационных файлов.

Tip

Для того, чтобы вывести результирующий объект конфигурации после его загрузки со всеми расширениями, воспользуйтесь консольной командой tadmin PrintJson.

Убедитесь, что в папке extensions расположены актуальные версии сборок с расширениями Tessa.Extensions.*.dll.

Загрузчик с методом Invoke для переопределения включаемых файлов

Посредством свойства ".loader.type" возможно указать квалифицированное имя типа для директивы .include или массив, содержащий имя типа с пространством имён и имя сборки.

Возможны следующие варианты указания типа:

  1. "ExampleNamespace.Loaders.ExampleConfigurationLoader, ExampleLoadersLib" - квалифицированное имя типа ExampleConfigurationLoader, указанное для сборки ExampleLoadersLib, которая может быть загружена по имени сборки (на которую ссылается приложение).
  2. [ "ExampleNamespace.Loaders.ExampleConfigurationLoader", "ExampleLoadersLib" ] - имя типа с пространством имён и имя сборки, аналогично п.1.
  3. [ "ExampleNamespace.Loaders.ExampleConfigurationLoader", "extensions/Tessa.Extensions.Shared.dll" ] - имя типа с пространством имён и имя файла со сборкой. Если указан относительный путь, то он рассчитывается от папки с конфигурационными файлами (соответствует папке с приложением). При этом приложение может не ссылаться на сборку с этим именем.

В этом случае, при наличии в нём метода Invoke, класс будет инстанциирован конструктором по умолчанию, а метод будет вызван. Если указанный тип не может быть загружен, то добавляется ошибка конфигурации.

Tip

Использование такого загрузчика позволяет создать библиотеку, содержащую логику загрузки, которая не ссылается на Tessa.dll, т.е. не использует никаких зависимых от платформы типов.

Метод Invoke получает следующие параметры:

  1. Первый параметр типа Dictionary<string, object> - это содержимое json-объекта в директиве .include, включая свойство ".loader.type".
  2. Второй параметр типа Dictionary<string, object> - это целиком содержание конфигурации на момент обработки (но без директивы .include).
  3. Параметр типа CancellationToken получает токен отмены операции.
  4. Все прочие параметры при их наличии получает значение null. Если они не допускают null (например, типы-значения), то вызов метода приведёт к ошибке.

Important

Если какие-либо из перечисленных параметров отсутствуют, то они не передаются. Например, если метод имеет единственный параметр Dictionary<string, object>, то параметры из п.2 не передаются.

Метод Invoke возвращает одно из следующих значений:

  1. Dictionary<string, object> - результирующий json-объект для объединения с текущим конфигурационным файлом, или null, если обработка не требуется. Метод вызывается синхронно.
  2. ValueTask<Dictionary<string, object>> - асинхронная задача, возвращающая json-объект для объединения с текущим конфигурационным файлом, или null, если обработка не требуется. Метод вызывается асинхронно.
  3. Task<Dictionary<string, object>> - асинхронная задача, аналогичная ValueTask в п.2.
  4. Все прочие возвращаемые значения игнорируются, т.е. аналогичны возврату null. Метод вызывается синхронно.

Пример такого класса:

using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json;

namespace ExampleNamespace.Loaders { public sealed class ExampleConfigurationLoader { public async ValueTask<Dictionary<string, object>> Invoke( Dictionary<string, object> parameters, Dictionary<string, object> storage, CancellationToken cancellationToken) { if (parameters.TryGetValue("uri", out var value) && value is string uri) { // зависимости необходимо создать вручную, DI недоступен string text; using (var httpClient = new HttpClient()) { text = await httpClient.GetStringAsync(uri, cancellationToken).ConfigureAwait(false); }

if (!string.IsNullOrEmpty(text)) { // в примере не ссылаемся на Tessa.dll, // поэтому напрямую используем библиотеку Newtonsoft.Json var serializer = new JsonSerializer(); using var reader = new JsonTextReader(new StringReader(text)); var settings = serializer.Deserialize<Dictionary<string, object>>(reader); return new Dictionary<string, object> { { "Settings", settings } }; } }

return null; } } }

Пример упрощённого класса, который получает только параметры, т.е. значение json-объекта в директиве .include, и синхронно возвращает хеш-таблицу.

using System.Collections.Generic; using System.IO; using System.Net.Http; using Newtonsoft.Json;

namespace ExampleNamespace.Loaders { public sealed class ExampleConfigurationLoader { public Dictionary<string, object> Invoke(Dictionary<string, object> parameters) { if (parameters.TryGetValue("uri", out var value) && value is string uri) { string text; using (var httpClient = new HttpClient()) { // для сетевых запросов нежелательно выполнять синхронную загрузку

// если бы метод изначально выполнял только синхронные действия, // то его имело бы смысл сделать синхронным

// здесь, для примера, мы асинхронный вызов преобразуем в синхронный

text = httpClient.GetStringAsync(uri).GetAwaiter().GetResult(); }

if (!string.IsNullOrEmpty(text)) { var serializer = new JsonSerializer(); using var reader = new JsonTextReader(new StringReader(text)); var settings = serializer.Deserialize<Dictionary<string, object>>(reader); return new Dictionary<string, object> { { "Settings", settings } }; } }

return null; } } }

Если класс расположен в сборке ExampleLoadersLib.dll, которая размещена в папке приложения, то его возможно использовать в файле app.json следующим образом:

{ ".include": [ { ".loader.type": [ "ExampleNamespace.Loaders.ExampleConfigurationLoader", "ExampleLoadersLib.dll" ], "uri": "https://my.configuration.server/files/app1.json" } ] }

Back to top