Ef core как получить iwebhostenvironment в модели
Для взаимодействия со средой, в которой запущено приложение, применяются объекты, реализующие интерфейс IHostingEnvironment . Этот интерфейс предлагает ряд свойств, с помощью которых мы можем получить информацию об окружении:
ApplicationName : возвращает имя приложения
EnvironmentName : возвращает описание среды, в которой хостируется приложение
ContentRootPath : возвращает путь к корневой папке приложения
WebRootPath : возвращает путь к папке, в которой хранится статический контент приложения, как правило, это папка wwwroot
ContentRootFileProvider : возвращает реализацию интерфейса Microsoft.AspNetCore.FileProviders.IFileProvider , которая может использоваться для чтения файлов из папки ContentRootPath
WebRootFileProvider : возвращает реализацию интерфейса Microsoft.AspNetCore.FileProviders.IFileProvider , которая может использоваться для чтения файлов из папки WebRootPath
При разработке мы можем использовать эти свойства. Но наиболее часто при разработке придется сталкиваться со свойством EnvironmentName . По умолчанию имеются три варианта значений для этого свойства: Development, Staging и Production. В проекте это свойство задается через установку переменной среды ASPNETCORE_ENVIRONMENT . Ее текущее значение можно посмотреть в свойствах проекта на вкладке Debug:
Здесь же также можно изменить значение этой переменной.
Кроме того, в проекте в папке Properties есть файл launchSettings.json , который также содержит описания переменных сред:
Здесь можно увидеть, что переменная «ASPNETCORE_ENVIRONMENT» встречается два раза — для запуска через IISExpress и для запуска через Kestrel. В обоих случаях она имеет значение Development. Здесь мы также можем поменять значение этой переменной. Причем файл launchSettings.json переопределяет настройки проекта. Например, если в launchSettings.json мы определим «ASPNETCORE_ENVIRONMENT»: «Production» , то имя среды нашего проекта будет «Production», хотя в настройках проекта может стоять «Development».
Для определения значения этой переменной для интерфейса IWebHostEnvironment определены специальные методы расширения:
IsEnvironment(string envName) : возвращает true , если имя среды равно значению параметра envName
IsDevelopment() : возвращает true , если имя среды — Development
IsStaging() : возвращает true , если имя среды — Staging
IsProduction() : возвращает true , если имя среды — Production
Например, при создании нового проекта в методе Configure() класса Startup можно найти следующие строки:
Таким образом, если имя среды имеет значение «Development», то есть приложение находится в состоянии разработки, то при ошибке разработчик увидит детальное описание ошибки. Если же приложение развернуто на хостинге и соответственно имеет другое имя хостирующей среды, то простой пользователь при ошибке ничего не увидит. Таким образом, в зависимости от стадии, на которой находится проект, мы можем скрывать или задействовать часть функционала приложения.
Определение своих состояний среды
Хотя по умолчанию среда может принимать три состояния: Development, Staging, Production, но мы можем при желании вводить новые значения. Например, нам надо отслеживать какие-то дополнительные состояния. К примеру, изменим в файле launchSettings.json значение «ASPNETCORE_ENVIRONMENT» на «Test» (значение может быть произвольное):
Далее мы можем использовать это состояния для выполнения различных действий. Например, используем состояние «Test» в классе Startup:
How to get the Development/Staging/production Hosting Environment in ConfigureServices
How do I get the Development/Staging/production Hosting Environment in the ConfigureServices method in Startup?
The ConfigureServices method only takes a single IServiceCollection parameter.
15 Answers 15
You can easily access it in ConfigureServices, just persist it to a property during Startup method which is called first and gets it passed in, then you can access the property from ConfigureServices.
Set an environment variable called ASPNETCORE_ENVIRONMENT with the name of the environment (e.g. Production ). Then do one of two things:
- Inject IHostingEnvironment into Startup.cs , then use that ( env here) to check: env.IsEnvironment(«Production») . Do not check using env.EnvironmentName == «Production» !
- Use either separate Startup classes or individual Configure / ConfigureServices functions. If a class or the functions match these formats, they will be used instead of the standard options on that environment.
- Startup
() (entire class) || example: StartupProduction() - Configure
() || example: ConfigureProduction() - Configure
Services() || example: ConfigureProductionServices()
Full explanation
The .NET Core docs describe how to accomplish this. Use an environment variable called ASPNETCORE_ENVIRONMENT that’s set to the environment you want, then you have two choices.
Внедрить IWebHostingEnvironment в переопределенный метод
Используя ASP.NET Core 5.0, я пытаюсь получить доступ IWebHostEnvironment к классу модели EF Core 5.0. Как я могу получить доступ к проверке IsDevelopment истинности из окружения?
Я вызываю следующий класс из своих контроллеров так:
Мне действительно нужно раскручивать IWebHostEnvironment каждый контроллер, который вызывает этот класс EF, чтобы выбрать правильный конструктор?
Я видел несколько угроз по поводу DI, но, честно говоря, у них есть запутанные решения, которые я не могу понять.
Исследуем .NET 6. Часть 3. Рассматриваем код WebApplicationBuilder
В предыдущем посте я сравнивал новый WebApplication с универсальным хостом. В этом посте я рассмотрю код, лежащий в основе WebApplicationBuilder , чтобы увидеть, как он обеспечивает более чистый, минимальный API хостинга, при этом обеспечивая ту же функциональность, что и универсальный хост.
WebApplication и WebApplicationBuilder: новый способ начальной загрузки приложений ASP.NET Core
В .NET 6 представлен совершенно новый способ «начальной загрузки» приложения ASP.NET Core. Вместо традиционного разделения между Program.cs и Startup.cs весь код находится в Program.cs и является гораздо более процедурным, чем множество лямбда-методов, которых требовал универсальный хост в предыдущих версиях:
В язык C# добавлены различные обновления, которые делают всё это более чистым (операторы верхнего уровня, неявные директивы using, выведение типов в лямбдах и т. д.). Но также появились два новых типа: WebApplication и WebApplicationBuilder . В предыдущем посте я кратко описал, как использовать WebApplication и WebApplicationBuilder для настройки приложения ASP.NET Core. В этом посте мы рассмотрим их код, чтобы увидеть, как получился более простой API, при сохранении той же гибкости и настраиваемости, что и у универсального хоста.
Создание WebApplicationBuilder
Первым шагом в нашем примере программы является создание экземпляра WebApplicationBuilder с использованием статического метода класса WebApplication :
Это создаёт новый экземпляр WebApplicationOptions , инициализирует аргументы Args из аргументов командной строки и передаёт объект параметров в конструктор WebApplicationBuilder (показанный кратко ниже):
Кстати, новое сокращённое ключевое слово new , когда целевой тип неочевиден, — просто отстой при разборе кода. Невозможно даже предположить, что создаёт второй new() в приведённом выше коде. Во многом претензии те же самые, как и к повсеместному использованию var , но в данном случае это меня особенно раздражает.
WebApplicationOptions предоставляет простой способ программного переопределения некоторых важных свойств. Если они не установлены, они будут выведены в значения по умолчанию, как и в предыдущих версиях .NET.
Конструктор WebApplicationBuilder – это то место, где происходит большая часть магии, чтобы заставить работать концепцию минимального хостинга:
Я ещё не показал тело метода, так как в этом конструкторе много чего происходит и используется много вспомогательных типов. Мы вернёмся к ним через секунду, а пока сосредоточимся на публичном API WebApplicationBuilder .
Публичный API WebApplicationBuilder
Публичный API WebApplicationBuilder состоит из набора свойств, доступных только для чтения, и одного метода Build() , который создаёт WebApplication .
Если вы знакомы с ASP.NET Core, вы заметите, что многие из этих свойств используют стандартные типы из предыдущих версий:
IWebHostEnvironment – используется для получения имени среды, пути к корню контента и подобных значений.
IServiceCollection – используется для регистрации сервисов в контейнере DI. Обратите внимание, что это альтернатива методу ConfigureServices() , используемому универсальным хостом для достижения того же результата.
ConfigurationManager – используется как для добавления новой конфигурации, так и для получения значений конфигурации. См. первый пост в серии, где обсуждается этот вопрос.
ILoggingBuilder – используется для регистрации дополнительных поставщиков журналов, как и в случае с методом ConfigureLogging() в универсальном хосте.
Свойства WebHost и Host интересны тем, что они предоставлены новыми типами ConfigureWebHostBuilder и ConfigureHostBuilder . Эти типы реализуют IWebHostBuilder и IHostBuilder соответственно и в основном представлены как замена для используемых ранее методов расширения.
Например, в предыдущем посте я показал, как можно зарегистрировать интеграцию Serilog с ASP.NET Core, вызвав UseSerilog() на свойстве Host :
Раскрытие интерфейсов IWebHostBuilder и IHostBuilder было абсолютно необходимо для обеспечения возможности перехода к новому минимальному API хостинга WebApplication , но это также оказалось проблемой. Как согласовать конфигурацию в виде лямбд/обратных вызовов в IHostBuilder с императивным стилем в WebApplicationBuilder ? Вот где в игру вступают ConfigureHostBuilder и ConfigureWebHostBuilder вместе с некоторыми внутренними реализациями IHostBuilder :
Мы начнём с рассмотрения публичных ConfigureHostBuilder и ConfigureWebHostBuilder .
ConfigureHostBuilder
ConfigureHostBuilder и ConfigureWebHostBuilder были добавлены как часть обновлений для минимального хостинга. Они реализуют IHostBuilder и IWebHostBuilder соответственно. В этом посте мы подробно рассмотрим ConfigureHostBuilder :
ConfigureHostBuilder реализует IHostBuilder , и похоже, что он реализует ISupportsConfigureWebHost , но взгляд на реализацию показывает, что это не так:
Это означает, что хотя следующий код и компилируется
он выбрасывает исключение NotSupportedException во время выполнения. Это явно не идеально, но это цена, которую мы платим за наличие хорошего императивного API для настройки сервисов и т. д. То же самое верно и для метода IHostBuilder.Build() – он выбросит NotSupportedException .
Видеть исключения времени выполнения никогда не доставляет радости, но это помогает думать о ConfigureHostBuilder как об «адаптере» для существующих методов расширения (таких как метод UseSerilog() ), а не как о «реальном построителе» хоста. Это становится очевидным, когда вы видите, как такие методы, как ConfigureServices() или ConfigureAppConfiguration() , реализованы для этих типов:
Например, метод ConfigureServices() немедленно выполняет предоставленный делегат Action<> , используя внедрённую коллекцию IServiceCollection из WebApplicationBuilder . Таким образом, следующие два вызова функционально идентичны:
Последний подход явно не стоит использовать в обычной практике, но существующий код, который полагается на этот метод (например, методы расширения), все ещё можно использовать.
Не все делегаты, переданные методам в ConfigureHostBuilder , запускаются немедленно. Некоторые из них, например UseServiceProviderFactory() , сохраняются в списке и выполняются позже при вызове WebApplicationBuilder.Build()
Вот примерно и всё про тип ConfigureHostBuilder . ConfigureWebHostBuilder очень похож и действует как адаптер от предыдущего API к новому императивному стилю. Теперь мы можем вернуться к конструктору WebApplicationBuilder .
Вспомогательный класс BootstrapHostBuilder
Прежде чем мы отвлеклись на ConfigureHostBuilder , мы собирались взглянуть на конструктор WebApplicationBuilder . Но мы еще не готовы к этому. сначала нужно взглянуть на ещё один вспомогательный класс, BootstrapHostBuilder .
BootstrapHostBuilder – это внутренняя реализация IHostBuilder , используемая в WebApplicationBuilder . В основном это относительно простая реализация, которая «запоминает» все полученные вызовы IHostBuilder . Например, функции ConfigureHostConfiguration() и ConfigureServices() выглядят следующим образом:
В отличие от ConfigureHostBuilder , который немедленно выполнял переданный делегат, BootstrapHostBuilder «сохраняет» переданные делегаты в список для последующего выполнения. Это похоже на то, как работает универсальный HostBuilder. Но обратите внимание, что BootstrapHostBuilder – это ещё одна «нестроящаяся» реализация IHostBuilder , в которой вызов Build() выдаёт исключение:
Большая часть сложности BootstrapHostBuilder заключается в его методе RunDefaultCallbacks(ConfigurationManager, HostBuilder) . Он используется для применения сохранённых делегатов в правильном порядке, как мы увидим позже.
Конструктор WebApplicationBuilder
Наконец, мы дошли до конструктора WebApplicationBuilder . Он содержит много кода, поэтому я буду рассматривать его по частям.
Обратите внимание, что я позволил себе вольность удалить второстепенные фрагменты кода (например, защитные конструкции и тестовый код).
Начнем с приватных полей и свойств. _hostBuilder – это экземпляр универсального хоста HostBuilder , который является «внутренним» хостом в WebApplicationBuilder . У нас также есть поле BootstrapHostBuilder (из предыдущего раздела) и экземпляр WebApplicationServiceCollection , являющийся реализацией IServiceCollection , которую я пока не буду рассматривать.
WebApplicationBuilder действует как «адаптер» для универсального хоста _hostBuilder, предоставляя императивный API, который я исследовал в моём предыдущем посте, сохраняя при этом ту же функциональность, что и универсальный хост.
К счастью, следующие шаги в конструкторе хорошо документированы:
После создания экземпляра BootstrapHostBuilder первым вызывается метод расширения HostingBuilderExtension.ConfigureDefaults() . Это тот же метод, который вызывается универсальным хостом при вызове Host.CreateDefaultBuilder() .
Обратите внимание, что аргументы не передаются в вызов ConfigureDefaults() , а вместо этого применяются позже. В результате на этом этапе аргументы не используются для настройки конфигурации хоста (конфигурация хоста определяет такие значения, как имя приложения и среда хостинга).
Следующий вызов метода GenericHostBuilderExtensions.ConfigureWebHostDefaults() , который является тем же методом расширения, который мы обычно вызываем при использовании универсального хоста в ASP.NET Core 3.x/5.
Этот метод по сути добавляет «адаптер» IWebHostBuilder поверх BootstrapHostBuilder , вызывает на нём WebHost.ConfigureWebDefaults() , а затем немедленно запускает переданный лямбда-метод. Это регистрирует метод WebApplicationBuillder.ConfigureApplication() для последующего вызова, который устанавливает целую кучу промежуточного ПО. Мы вернёмся к этому методу в следующем посте.
После настройки веб-хоста следующий метод применяет аргументы к конфигурации хоста, гарантируя, что они правильно переопределяют значения по умолчанию, установленные предыдущими методами расширения:
Наконец, вызывается метод BootstrapHostBuilder.RunDefaultCallbacks() , который запускает все сохранённые обратные вызовы, которые мы накопили до сих пор, в правильном порядке, чтобы построить HostBuilderContext . Затем HostBuilderContext используется для окончательной установки остальных свойств в WebApplicationBuilder .
Это конец конструктора. На этом этапе приложение настроено со всеми настройками по умолчанию для «хостинга»: конфигурация, ведение журнала, службы DI, среда и т. д.
Теперь вы можете добавить все свои собственные сервисы, дополнительную конфигурацию или ведение журнала в WebApplicationBuilder :
Закончив настройку конкретного приложения, вы вызываете Build() для создания экземпляра WebApplication . В последнем разделе этого поста мы заглянем внутрь метода Build() .
Построение WebApplication в WebApplicationBuilder.Build()
Метод Build() не очень длинный, но его немного сложно понять, поэтому я рассмотрю его построчно:
Первое, что мы делаем, это копируем источники конфигурации, настроенные в ConfigurationManager , в реализацию ConfigurationBuilder _hostBuilder . Когда вызывается этот метод, построитель изначально пуст, поэтому он заполняет все источники, которые были добавлены как методами расширения построителя по умолчанию, так и дополнительные источники, которые вы настроили впоследствии.
Обратите внимание, что технически метод ConfigureHostConfiguration запускается не сразу. Скорее, мы регистрируем обратный вызов, который будет вызываться, когда мы далее вызовем _hostBuilder.Build() .
Затем мы делаем то же самое для IServiceCollection , копируя их из экземпляра _services в коллекцию _hostBuilder . Комментарии здесь достаточно выразительные. В этом случае коллекция сервисов _hostBuilder не всегда пуста, но мы добавляем в неё всё из Services, а затем «сбрасываем» Services в экземпляр _hostBuilder .
В следующей строке мы запускаем все обратные вызовы, которые мы собрали в свойстве ConfigureHostBuilder , как я показывал выше.
Если они есть, то это обратные вызовы, такие как ConfigureContainer() и UseServiceProviderFactory() , которые обычно используются только в том случае, если вы используете сторонний контейнер DI.
Наконец, мы вызываем _hostBuilder.Build() для создания экземпляра Host и передаём его новому экземпляру WebApplication . Вызов _hostBuilder.Build() – это то место, где вызываются все зарегистрированные обратные вызовы.
Теперь нам надо сделать небольшую уборку. Чтобы всё было согласовано, экземпляр ConfigurationManager очищается и связывается с конфигурацией, хранящейся в WebApplication . Кроме того, IServiceCollection в WebApplicationBuilder помечается как доступный только для чтения, поэтому попытка добавить службы после вызова WebApplicationBuilder вызовет исключение InvalidOperationException . Наконец, возвращается построенное веб-приложение.
Это почти всё для WebApplicationBuilder , но мы ещё не выполнили обратный вызов ConfigureApplication() . В следующем посте мы рассмотрим код, лежащий в основе типа WebApplication , и увидим, где, наконец, вызывается ConfigureApplication() .
Итого
В этом посте мы рассмотрели часть кода нового API минимального хостинга WebApplicationBuilder . Я показал, как типы ConfigureHostBuilder и ConfigureWebHostBuilder действуют как адаптеры для универсального хоста и как BootstrapHostBuilder используется в качестве оболочки для внутреннего HostBuilder . Было довольно много запутанного кода только для создания экземпляра WebApplicationBuilder , но мы закончили статью, вызвав Build() для создания WebApplication . В следующем посте мы рассмотрим код WebApplication .
- Startup