В .NET Core появилось много новых функций, которые разработчики использовали и которые понравились разработчикам с момента выпуска фреймворка. Было внесено множество улучшений в производительность, совместимость и архитектуру фреймворка.

В этой статье я исследую некоторые функции, связанные с запуском приложений, которые немного скрыты в документации Microsoft ASP.NET Core, но могут быть очень полезны для обычного разработчика.

Запуск приложения

В ASP .NET Core часто используемый файл Global.asax был удален в пользу двух классов: Program и Startup. Класс Program обычно не претерпевает больших изменений от проекта к проекту. Он содержит метод Main, используемый в качестве точки входа для приложения, а в .NET Core 2.0 весь класс можно проиллюстрировать следующим образом:

Все просто, правда? Все, что он делает, - это создает веб-хостинг на основе заданных вами настроек и запускает его. Однако этот класс не несет ответственности за конфигурацию и настройки. Это делается в классе Startup (или в любом другом классе с желаемым именем, если он настроен в (1)). Вот где происходит волшебство.

Класс стартапа

Начиная с .NET Core, инверсия управления (IoC) и, следовательно, внедрение зависимостей (DI) предлагаются из коробки. Это упрощает настройку полнофункционального веб-приложения без установки какой-либо сторонней инфраструктуры IoC.

Для большинства распространенных сценариев этот подход работает, но есть случаи, когда сторонняя инфраструктура IoC, такая как Autofac, может подойти лучше.

Одна из основных задач этого класса - установка зависимостей, которые будут использоваться во всем приложении. И здесь вам пригодится магия условностей! Но обо всем по порядку. Так выглядит только что созданный класс Startup:

Конструктор класса (1) принимает объект конфигурации, который обычно загружается из файла appsettings.json, и среду размещения, в которой ваше приложение работает на. Затем есть два метода, которые наиболее актуальны для вашего приложения и выполняются по порядку:

  • ConfigureServices (2) - который получает экземпляр сбора службы. Этот метод выполняется сразу после конструктора, и именно здесь вы можете настроить свои зависимости;
  • Настроить (3) - отвечает за настройку конвейера приложения. В этом случае он получает конструктор приложения, но в значительной степени может получать в качестве параметра любую зависимость, которую вы настроили ранее в методе ConfigureServices. Вы также можете получить настроенные ранее службы с помощью свойства IApplicationBuilder.IServiceProvider.

Как видно на примере (4), есть предложение неприятно, но полезно if, которое проверяет, запущено ли приложение в Режим разработки и вводит в конвейер приложения обязательство использовать подробную страницу исключения вместо страницы 500-failed-contact-admin.

Если у нашего приложения есть много режимов для запуска, например, разработка, интеграционные тесты, контроль качества и производство - всего 4 режима, в пределе у нас может быть такое же количество if-предложений, если все режимы диктовали какие-то разные зависимости.

Например, вам могут потребоваться другие стратегии репозитория, когда вы разрабатываете свое приложение локально. С другой стороны, вы можете захотеть иметь репозитории, которые обращаются к базе данных, пока вы выполняете интеграционные тесты. Или, может быть, в разработке вы можете не захотеть на самом деле спамить ящик электронной почты каждый раз, когда вы что-то тестируете, если ваше приложение отправляет электронные письма. Но вы, вероятно, хотите, чтобы это делал режим QA. В результате вы получите что-то вроде этого:

Разумеется, такой подход работает. Но поскольку я помешан на организации, этот код заставляет меня нервничать, даже если он используется только для запуска приложения!

Переменная, которая управляет режимом, в котором работает ваше приложение, - это переменная среды ASPNETCORE_ENVIRONMENT, которая может быть либо общесистемной, либо для конкретного приложения. Эту переменную также можно ввести с помощью определенного профиля приложения, расположенного в launchSettings.json в папке src / Properties:

Как вы, возможно, уже заметили, есть несколько методов расширения для IHostingEnvironment, которые просто обертывают его проверку EnvironmentName: IsDevelopment, IsProduction , IsStaging или IsEnvironment, в котором в последнем вам необходимо указать ожидаемое имя настраиваемой среды.

Эта переменная среды также определяет, какой appsettings.json вы будете использовать. Если вы установите для переменной среды значение Разработка, он будет использовать файл appsettings.Development.json, и все свойства, которые не найдены в этом, будут заменены на существующие. в appsettings.json по умолчанию.

Это круто, правда? Возможность переопределить конфигурацию системы выбранным вами режимом - хороший подход. Обратите внимание, что на самом деле мы не используем if-clauses, когда дело доходит до выбора правильного файла appsettings.json. Все это делается фреймворком с использованием соглашений. Разве было бы неплохо, если бы вы могли сделать то же самое в своем коде?

Да было бы. И вы действительно можете это сделать. Именно тогда в дело вступает магия конвенций!

Запуск конвенционного приложения

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

Первый путь

Один из способов использовать подход, основанный на соглашении о запуске, - это изменить методы, которые инициализируют наше приложение.

Два метода запуска, Configure и ConfigureServices, считываются инициализатором приложения платформы тогда и только тогда, когда, не существует настраиваемых методов, соответствующих режиму, в котором работает приложение. Это означает, что вы можете изменить имена методов на следующие форматы соответственно:

  • Настроить {EnvironmentName}
  • Настройте службы {EnvironmentName}

Например: ConfigureDevelopment и ConfigureDevelopmentServices. Но вы можете изменить его на любое имя настраиваемой среды, например ConfigureQAMode и ConfigureQAModeServices, если ваш EnvironmentName - QAMode!

Достаточно сказать, что это избавляет от необходимости проверять переменную среды во время выполнения в рамках определенного метода инициализации. Теперь все можно делать в конкретном методе, который соответствует текущему режиму работы приложения!

Пример, основанный на предыдущем коде инициализации:

Вот и все! Чистота! И больше никаких изменений вносить не нужно! Обратите внимание, что, как я уже сказал, если фреймворк не находит никаких методов инициализации для текущего режима, он будет использовать ConfigureServices и Configure по умолчанию. Поэтому, если есть несколько режимов приложения, для которых вы не хотите указывать метод, просто оставьте режимы по умолчанию.

Второй путь

Другой способ добиться этого - присвоить соглашение классу Startup. Это означает, что в вашем проекте может быть несколько классов Startup: StartupDevelopment, StartupProduction и так далее. Имена методов Configure и ConfigureServices в этих классах могут быть именованными по умолчанию, поскольку соглашение теперь стоит в имени класса.

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

Изменение в (1) теперь заключается в загрузке файла Startup на основе текущей сборки. Затем инфраструктура найдет класс Startup {EnvironmentName} и использует его или вернется к классу по умолчанию, Startup.

Лично я не являюсь поклонником этого подхода, поскольку он увеличивает вероятность дублирования кода, даже если вы можете использовать наследование (например, Startup {EnvironmentName}, расширяющее значение по умолчанию Startup ). Но все же я предпочитаю использовать первый способ запуска по соглашению, смешанный с функцией частичного класса, чтобы уменьшить раздувание кода в одном файле и увеличить совместное использование кода.

Итак, мой любимый подход следующий:

  • Создайте столько файлов классов запуска, сколько режимов вы хотите специально настроить, плюс один для запасного варианта с именем по умолчанию (например, Startup, StartupDevelopment.cs , StartupQAMode.cs и т. д.…);
  • В каждом файле определите частичный класс с именем Startup и, в зависимости от того, какому режиму соответствует имя файла, создайте определенные методы Configure и ConfigureServices. там (например, ConfigureServices, ConfigureDevelopmentServices, ConfigureQAModeServices и т. д.…);
  • Для общих и резервных конфигураций используйте файл Startup.cs. Обратите внимание, что в предыдущем примере кода запуска мы повторили строку services.AddMvc (). Это нормально для одной строки, но если у вас есть десятки общих зависимостей, вы можете создать общий метод, который будет вызывать каждый конкретный метод Configure или ConfigureServices.

Полный пример в нашем сценарии:

Вот и все, ребята! Надеюсь, вам понравилось!

Дополнительные сведения см. В следующих документах Microsoft: