C# 8.0 шаблон ссылок и необнуляемых значений - PullRequest
2 голосов
/ 25 апреля 2020

Tl; dr: Мне нужен класс параметров, который использует ненулевые типы для своих элементов без значений по умолчанию.

C# 8.0 представляет Обнуляемые ссылочные типы.

Я обнаружил, что использование обнуляемых ссылочных типов с шаблоном ASP. Net параметров довольно сложно, неполно или я что-то упускаю. Я испытываю ту же проблему, описанную в этом стеке над сообщением о потоке .

  1. Мы не хотим делать Name обнуляемым, поскольку тогда нам нужно разместить традиционные проверки на ноль везде (что противоречит назначению необнуляемых ссылочных типов)
  2. Мы не можем создать конструктор для принудительного создания класса MyOptions с ненулевым значением имени, так как метод Configure создает экземпляр параметров для нас
  3. Мы не можем использовать уловку прощающего оператора (publi c string name {get; set;} = null !;), так как тогда мы не можем гарантировать, что свойство Name установлено и мы можем в конечном итоге получить нулевое значение в свойстве Name, где этого не следует ожидать (внутри служб)

Я хочу класс параметров, который использует ненулевые типы для своих члены без значений по умолчанию. Ответы в этом посте заканчиваются тем, что в любом случае используются обнуляемые типы (которых я пытаюсь избежать) или значения по умолчанию (которых я также пытаюсь избежать).

Комментарии a но проверка параметров выявляет хорошие моменты и выглядит многообещающе, но оказывается, что для метода Validate все еще нужен объект параметров для проверки, который побеждает цель, если вам уже нужно передать объект параметров в it.

public ValidateOptionsResult Validate(string name, MyOptions options)
 // Pointless if MyOptions options is being passed in here

Это бессмысленно, потому что я определил, что единственный способ принудительного применения класса параметров со всеми ненулевыми членами и без значений по умолчанию - иметь конструктор. Возьмите, например, приведенный ниже пример кода.

namespace SenderServer.Options
{
    using System;
    using Microsoft.Extensions.Configuration;

    /// <summary>
    /// Configuration options for json web tokens.
    /// </summary>
    public class JwtOptions
    {
        /// <summary>
        /// The secret used for signing the tokens.
        /// </summary>
        public String Secret { get; }

        /// <summary>
        /// The length of time in minutes tokens should last for.
        /// </summary>
        public Int32 TokenExpirationInMinutes { get; }

        /// <summary>
        /// Configuration options for json web tokens.
        /// </summary>
        /// <param name="secret"> The secret used for signing the tokens.</param>
        /// <param name="tokenExpirationInMinutes">The length of time in minutes tokens should last for.</param>
        public JwtOptions(String secret, Int32 tokenExpirationInMinutes)
        {
            Secret = secret;
            TokenExpirationInMinutes = tokenExpirationInMinutes;
        }

        /// <summary>
        /// Create a JwtOptions instance from a configuration section.
        /// </summary>
        /// <param name="jwtConfiguration">The configuration section.</param>
        /// <returns>A validated JwtOptions instance.</returns>
        public static JwtOptions FromConfiguration(IConfiguration jwtConfiguration)
        {
            // Validate the secret
            String? secret = jwtConfiguration[nameof(Secret)];
            if (secret == null)
            {
                throw new ArgumentNullException(nameof(Secret));
            }

            // Validate the expiration length
            if (!Int32.TryParse(jwtConfiguration[nameof(TokenExpirationInMinutes)], out Int32 tokenExpirationInMinutes))
            {
                throw new ArgumentNullException(nameof(TokenExpirationInMinutes));
            }

            if (tokenExpirationInMinutes < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(TokenExpirationInMinutes));
            }

            return new JwtOptions(secret, tokenExpirationInMinutes);
        }
    }
}

Так что, если мне понадобится конструктор с параметрами для класса, то я могу создать его самостоятельно с чем-то вроде:

// Configure the JWT options
IConfiguration jwtConfiguration = Configuration.GetSection("Authentication:JwtOptions");
JwtOptions jwtOptions = JwtOptions.FromConfiguration(jwtConfiguration); // This performs validation as well

но тогда куда мне положить jwtOptions? Ни один из services.Configure<JwtOptions>(jwtOptions); и вариантов просто не принимает уже созданный экземпляр объекта (или, по крайней мере, ни один из тех, что я видел). И, наконец, даже если они это сделали, вы не можете использовать класс опций с внедрением зависимостей, который не имеет общедоступного c конструктора без параметров.

public JwtService(IOptions<JwtOptions> jwtOptions)

1 Ответ

1 голос
/ 25 апреля 2020

Мне нужен класс параметров, который использует ненулевые типы для своих членов без значений по умолчанию.

Тогда, к сожалению, Microsoft.Extensions.Options просто не для вас. Опционы работают так, что имеют конвейер конфигурации из нескольких источников, действий и валидаторов, которые работают с одним и тем же объектом опций. Поскольку явного начала этого конвейера нет, и любой источник конфигурации может находиться в любой позиции в конвейере, конструкция объекта параметров обрабатывается платформой и происходит до того, как будет вызван любой из источников конфигурации. .

Это строго необходимо для того, чтобы Опции позволили использовать различные варианты использования: Вы можете настроить параметры из конфигурации (Microsoft.Extensions.Configuration), вы можете настроить их с помощью действий конфигурации, вы можете настройте их через службы, которые имеют дополнительные зависимости, и т. д. c. И все они могут выполняться в любом порядке.

Таким образом, поскольку создание объекта происходит с помощью каркаса, также должны быть значения по умолчанию, с которыми создается объект параметров: Обычно это просто тип default значение, но вы также можете выбрать другие значения по умолчанию через конструктор объекта.

Если вы хотите применить указанные параметры c после конвейера, вы можете использовать действия после настройки для применения конфигурации или проверка параметров для проверки настроенных параметров. Но так как все это выполняется в конвейере, вам нужно иметь значения по умолчанию.

Таким образом, в основном, если вам нужно иметь ненулевые свойства без значений по умолчанию, вы не можете использовать Options. По крайней мере, не из коробки. Если вы хотите сделать это для того, чтобы безопасно ссылаться на опции в ваших сервисах, то есть другой способ приблизиться к этому: вместо внедрения IOptions<T>, введите объект необязательных параметров T напрямую. И предоставьте это через фабрику:

services.AddSingleton<MySafeOptions>(sp =>
{
    var options = sp.GetService<IOptions<MyUnsafeOptions>>();
    return new MySafeOptions(options.Value);
});
services.Configure<MyUnsafeOptions>(Configuration.GetSection("MyOptions"));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...