.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1044. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 1
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.

Заглянем в ConfigurationManager
ConfigurationManager был добавлен для поддержки новой модели WebApplication в ASP.NET. Однако он во многом является деталью реализации.

В .NET5 двумя основными типами конфигурации:
- IConfigurationBuilder - для добавления источников конфигурации. Вызов Build() считывает каждый из источников конфигурации и строит окончательную конфигурацию.
- IConfigurationRoot - представляет собой окончательную «построенную» конфигурацию.

Поставщики конфигурации обычно включают методы расширения (например, AddJsonFile() и AddAzureKeyVault()), которые добавляют источник конфигурации в список источников. IConfigurationRoot в свою очередь объединяет все значения из каждого источника конфигурации, чтобы дать окончательное представление всех значений.

Проблема "частичной сборки конфигурации" в .NET 5
Основная проблема с этим подходом проявляется, когда вам нужно построить конфигурацию «частично». Это распространённая проблема, когда вы храните свою конфигурацию в таком сервисе, как Azure Key Vault, или в базе данных. Рекомендуемый подход состоит в следующем:
- Добавить «начальные» значения конфигурации.
- Создать «частичный» результат конфигурации, вызвав IConfigurationBuilder.Build()
- Получить необходимые значения конфигурации из построенного IConfigurationRoot
- Использовать эти значения, чтобы добавить оставшиеся источники конфигурации
- Фреймворк неявно вызывает IConfigurationBuilder.Build(), генерируя окончательный IConfigurationRoot и используя его для окончательной конфигурации приложения.

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

Менеджер Конфигурации в .NET 6
В .NET 6 добавлен новый тип конфигурации, ConfigurationManager. Он реализует как IConfigurationBuilder, так и IConfigurationRoot, позволяя оптимизировать сценарий, описанный выше.
В ConfigurationManager, когда добавляется IConfigurationSource (например, когда вы вызываете AddJsonFile()), поставщик сразу загружается, и конфигурация обновляется. Это позволяет избежать загрузки источников конфигурации более одного раза в сценарии частичной сборки.

Однако ConfigurationManager оптимизирован для наиболее распространённого сценария – только добавление источников. Если какой-либо из источников изменится, ConfigurationManager должен удалить всё и начать заново, перебирая каждый из источников и перезагружая их.

Стоит ли вам беспокоиться о том, используете ли вы ConfigurationManager или ConfigurationBuilder? Скорее нет.

Новый WebApplicationBuilder, представленный в .NET 6, использует ConfigurationManager, оптимизированный для описанного выше случая, когда вам необходимо частично построить конфигурацию.

Однако WebHostBuilder или HostBuilder, представленные в более ранних версиях ASP.NET, по-прежнему поддерживаются в .NET 6, и они по-прежнему «за кулисами» используют типы ConfigurationBuilder и ConfigurationRoot.

Подробнее перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-1-looking-inside-configurationmanager-in-dotnet-6/
👍1
День 1048. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 2
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Часть 1

WebApplicationBuilder
В .NET появился новый способ «по умолчанию» для создания приложений, используя WebApplication.CreateBuilder(). Во всех предыдущих версиях ASP.NET Core конфигурация разделена на 2 файла: Program.cs и Startup.cs (см. рисунок ниже). В .NET 6 добавлено множество изменений в C#, BCL и ASP.NET Core, и теперь всё может быть в одном файле.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();

var app = builder.Build();
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.MapRazorPages();
app.Run();

WebApplicationBuilder
Для начала рассмотрим WebApplicationBuilder.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();

Он отвечает за 4 основные вещи:
1. Добавление конфигурации с помощью builder.Configuration. Он предоставляет тип ConfigurationManager для добавления новых источников конфигурации, а также для доступа к значениям конфигурации, как я описал в предыдущем посте.

2. Добавление сервисов с помощью builder.Services. Предоставляет доступ к IServiceCollection напрямую для добавления сервисов в контейнер DI.

3. Аналогично для журнала с помощью builder.Logging. Вы можете использовать, Например:
builder.Logging.AddFile();

4. Общая конфигурация IHostBuilder и IWebHostBuilder. Для тех точек расширения, которые напрямую зависят от IHostBuilder или IWebHostBuilder, WebApplicationBuilder предоставляет свойства Host и WebHost соответственно. Например, настройка Serilog для ASP.NET Core подключается к IHostBuilder. С помощью WebApplicationBuilder вы можете вызвать UseSerilog() на свойстве Host:
builder.Host.UseSerilog();

Фактически, WebApplicationBuilder – это то место, где вы выполняете всю настройку, кроме конвейера промежуточного ПО.

WebApplication
После того, как вы настроили всё необходимое в WebApplicationBuilder, вы вызываете Build() для создания экземпляра WebApplication:
var app = builder.Build();

WebApplication реализует несколько различных интерфейсов:
- IHost – для запуска и остановки хоста.
- IApplicationBuilder – для создания конвейера промежуточного ПО.
- IEndpointRouteBuilder – для добавления конечных точек.

Два последних пункта во многом связаны. В ASP.NET Core 3.x и 5 IEndpointRouteBuilder используется для добавления конечных точек путем вызова UseEndpoints() и передачи ему лямбды, например:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}

WebApplication значительно упрощает этот шаблон:
app.UseStaticFiles();
app.MapRazorPages();

Это явно намного проще, хотя немного обманчиво, поскольку различие между промежуточным ПО и конечными точками гораздо менее очевидно, чем в .NET 5.x. Вероятно, это просто дело вкуса, но такой подход несколько размывает концепцию важности порядка настройки (которая относится к промежуточному ПО, но не к конечным точкам).

Подробнее перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-2-comparing-webapplicationbuilder-to-the-generic-host/
День 1052. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 3
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Часть 1
Часть 2

Рассматриваем код WebApplicationBuilder
Напомню стандартный шаблон нового минимального API приложения ASP.NET Core:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// добавляем конфигурацию
builder.Configuration.AddJsonFile("sharedsettings.json");

// добавляем сервисы
builder.Services.AddSingleton<MyTestService>();
builder.Services.AddRazorPages();

// добавляем ведение журнала
builder.Logging.AddFile();

// строим!
WebApplication app = builder.Build();

app.UseStaticFiles();

// добавляем конечные точки
app.MapGet("/", () => "Hello World!");
app.MapRazorPages();

// запускаем!
app.Run();

Первым шагом является создание экземпляра WebApplicationBuilder с использованием статического метода класса WebApplication:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

Ниже приведены приватные и публичные члены класса WebApplicationBuilder:
public sealed class WebApplicationBuilder
{
private readonly HostBuilder _hostBuilder = new();
private readonly BootstrapHostBuilder _bootstrapHostBuilder;
private readonly WebApplicationServiceCollection _services = new();

public IWebHostEnvironment Environment { get; }
public IServiceCollection Services { get; }
public ConfigurationManager Configuration { get; }
public ILoggingBuilder Logging { get; }

public ConfigureWebHostBuilder WebHost { get; }
public ConfigureHostBuilder Host { get; }

public WebApplication Build()
}

Публичный API состоит из набора свойств только для чтения, и метода Build(), который создаёт WebApplication.

Многие из этих свойств используют стандартные типы из предыдущих версий:
- IWebHostEnvironment – используется для получения имени среды, пути к корню контента и подобных значений.
- IServiceCollection – используется для регистрации сервисов в контейнере DI. Обратите внимание, что это альтернатива методу ConfigureServices(), используемому универсальным хостом для достижения того же результата.
- ConfigurationManager – используется как для добавления новой конфигурации, так и для получения значений конфигурации. См. первый пост в серии, где обсуждается этот вопрос.
- ILoggingBuilder – используется для регистрации дополнительных поставщиков журналов, как и в случае с методом ConfigureLogging() в универсальном хосте.
- Свойства WebHost и Host интересны тем, что они предоставлены новыми типами ConfigureWebHostBuilder и ConfigureHostBuilder. Эти типы реализуют IWebHostBuilder и IHostBuilder соответственно и в основном представлены как замена для используемых ранее методов расширения.

Приватные члены класса содержат:
- вспомогательный класс BootstrapHostBuilder, который «запоминает» переданные ему делегаты конфигурации для последующего их вызова в правильном порядке при построении хоста. Кроме того, он вызывает знакомые нам по .NET 3.x/5 методы расширения CreateDefaultBuilder и ConfigureWebHostDefaults.
- HostBuilder – экземпляр универсального хоста, который является «внутренним» хостом в WebApplicationBuilder. WebApplicationBuilder действует как «адаптер» для универсального хоста _hostBuilder, предоставляя императивный API и сохраняя при этом ту же функциональность, что и универсальный хост.
См. рисунок ниже.

В методе Build() вся конфигурация, сделанная по умолчанию, а также добавленная пользователем в файле Program.cs, передаётся в _hostBuilder в правильном порядке, и возвращается новый экземпляр WebApplication, в конструктор которого передаётся построенный «внутренний» хост:
_builtApplication = new WebApplication(_hostBuilder.Build());

return _builtApplication;

Подробнее перевод статьи с примерами кода размещён на Хабре.

Источник:
https://andrewlock.net/exploring-dotnet-6-part-3-exploring-the-code-behind-webapplicationbuilder/
День 1058. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 4
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Часть 1
Часть 2
Часть 3

Рассматриваем код WebApplication
Сегодня мы немного рассмотрим код, лежащий в основе WebApplication, и сосредоточимся на настройке промежуточного ПО и конечных точек.

В предыдущем посте мы остановились на том, на WebApplicationBuilder вызывался метод Build(), который создавал WebApplication, передавая в его конструктор построенный хост:
_builtApplication = new WebApplication(_hostBuilder.Build());

return _builtApplication;

Класс WebApplication
public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteBuilder, IAsyncDisposable
{
}

По сравнению с конструктором WebApplicationBuilder, конструктор WebApplication относительно прост. Конструктор и инициализаторы полей выполняют 4 основные задачи:
1. Сохранить предоставленный хост в поле _host. Это тот же тип хоста, что и при прямом использовании универсального хоста, как в ASP.NET Core 3.x/5.
2. Создать новый список EndpointDataSource. Источники конечных точек используются для настройки конечных точек в приложении, включая Razor Pages, контроллеры, конечные точки API и новые «минимальные» API.
3. Создать новый экземпляр ApplicationBuilder. Он используется для создания конвейера промежуточного ПО.
4. Установить свойство __GlobalEndpointRouteBuilder в ApplicationBuilder. Это свойство используется для «сообщения» другому промежуточному ПО, что мы используем новый WebApplication, у которого немного другие значения по умолчанию.

WebApplication в основном делегирует реализацию IHost, IApplicationBuilder и IEndpointRouteBuilder своим приватным свойствам и реализует эти интерфейсы явно.

Конвейер промежуточного ПО в WebApplication
Одно из серьёзных отличий WebApplication от универсального хоста заключается в том, что WebApplication по умолчанию устанавливает различное промежуточное ПО.

Рассмотрим простой пример приложения:
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
WebApplication app = builder.Build();

app.UseHttpsRedirection();
app.UseStaticFiles();

app.MapGet("/", () => "Hello World!");
app.Run();

Здесь создаётся следующее промежуточное ПО (см. рисунок ниже):
1. HostFilteringMiddleware – добавляется благодаря скрытому вызову ConfigureWebHostDefaults(), как и в универсальном хосте в предыдущих версиях.
2. ForwardedHeadersMiddleware – добавляется там же, если для переменной среды ASPNETCORE_FORWARDEDHEADERS_ENABLED задано значение true (не в нашем случае).
3. DeveloperExceptionPageMiddleware – теперь добавляется автоматически, когда вы работаете в среде разработки (Development):
app.UseDeveloperExceptionPage().
4. EndpointRoutingMiddleware (также известное как RoutingMiddleware) – теперь автоматически добавляется в конвейер промежуточного ПО до начала конвейера в Program.cs, а EndpointMiddleware автоматически добавляется в конвейер в конце.
5. Конвейер WebApplication.ApplicationBuilder, содержащий всё промежуточное ПО, определённое в Program.cs:
- HttpsRedirectionMiddleware
- StaticFilesMiddleware
6. EndpointMiddleware – теперь автоматически добавляется в конвейер в конце.

Добавление конечной точки с помощью MapGet() добавляет запись в коллекцию EndpointDataSource объекта WebApplication, что приводит к автоматическому добавлению промежуточного ПО конечных точек:
- EndpointRoutingMiddleware выбирает конечную точку,
- EndpointMiddleware исполняет конечную точку.
EndpointMiddleware обычно добавляется в конвейер в конце путем вызова UseEndpoints(), но здесь оно добавляется автоматически.

Подробнее перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-4-building-a-middleware-pipeline-with-webapplication/
День 1063. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 5
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Часть 1
Часть 2
Часть 3
Часть 4

Поддержка EF Core в WebApplicationBuilder
EF Core включает в себя различные инструменты для создания миграций и запуска их в вашей базе данных, но для этого ему необходимо понимать ваш код. По сути, он должен иметь возможность исполнять код запуска вашего приложения, чтобы использовать все настроенные вами сервисы конфигурации и внедрения зависимостей.

В предыдущих версиях ASP.NET Core EF Core подключался к методу CreateWebHostBuilder или CreateHostBuilder в вашем классе Program. Инструменты EF Core искали этот «волшебный» метод для получения доступа к IWebHostBuilder или IHostBuilder, используемый для создания вашего приложения. Так инструменты EF Core могли загрузить сборку вашего приложения с помощью рефлексии, выполнить этот метод, получить возвращённый IHostBuilder, вызвать на нём Build() для создания IHost, а затем получить IServiceProvider из свойства IHost.Services, и, уже используя этот провайдер, проанализировать практически всё, что связано с вашим приложением.

Поддержка инструментов EF Core в .NET 6
Чтобы обойти изменения, внесённые в .NET 6, в HostBuilder были добавлены новые события DiagnosticSource. Они позволяют подписчикам получить доступ к HostBuilder непосредственно перед его построением, а также доступ к экземпляру IHost сразу после его создания:
1. На время действия метода HostBuilder.Build() создаётся новый DiagnosticListener.
2. Если что-либо прослушивает событие Microsoft.Extensions.Hosting.HostBuilding (хост строится), HostBuilder передает себя (this) слушателю в качестве параметра метода diagnosticListener.Write().
3. После создания хоста и перед возвратом из метода HostBuilder проверяет, прослушивает ли что-нибудь событие Microsoft.Extensions.Hosting.HostBuilt (хост построен). Если это так, вновь созданный хост передаётся слушателю в качестве параметра того же метода diagnosticListener.Write().

Вот как инструменты EF Core используют эти события (см. схему ниже):
1. Загружается сборка вашего приложения.
2. Создаётся экземпляр вспомогательного класса HostFactoryResolver, который ищет и исполняет точку входа в приложение (например, Program.Main).
3. Точка входа запускается в фоновом потоке. Это запускает ваше приложение. И предполагается, что в какой-то момент ваше приложение вызовет HostBuilder.Build().
4. Идёт ожидание одного из трёх событий:
- Код слушателя выдаёт исключение StopTheHostException после завершения всей настройки (об этом ниже).
- В запущенном приложении возникает иное исключение.
- Запуск приложения занимает слишком много времени, поэтому прерывается по таймауту.
5. Результат (в виде построенного хоста или в виде сгенерированного исключения) передаётся инструментам EF Core.

Пара слов о StopTheHostException. Запущенное в фоновом потоке приложение после постройки хоста может продолжать работу, и, например, начать прослушивать порты. Чтобы этого не происходило, слушатель событий, находящийся в том же потоке, после получения события HostBuilt (хост построен), выбрасывает исключение StopTheHostException, которое не обрабатывается, вызывая завершение приложения.

Подробный перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-5-supporting-ef-core-tools-with-webapplicationbuilder/
День 1058. #ЗаметкиНаПолях #ExploringNET6
Исследуем .NET 6. Часть 6
В этой серии статей рассмотрим подробно некоторые из новых функций, которые появились в .NET 6.
Предыдущие части: 1, 2, 3, 4, 5

Поддержка интеграционных тестов в WebApplicationFactory
В предыдущем посте мы рассмотрели изменения, внесённые в HostBuilder для поддержки HostFactoryResolver, используемого инструментами EF Core. Это было достигнуто за счёт добавления дополнительных событий DiagnosticSource в HostBuilder. Они позволяют HostFactoryResolver получить доступ к HostBuilder без необходимости использовать соглашения предыдущих версий.

Инструментам EF Core просто необходимо получить доступ к построенному IHost, чтобы извлечь из него IServiceProvider, поэтому запущенное фоновое приложение останавливалось. Однако WebApplicationFactory должна иметь возможность модифицировать HostBuilder вашего приложения до вызова Build(). Кроме того, ей требуется, чтобы приложение продолжало работать для отправки в него тестовых запросов.

WebApplicationFactory предоставляет несколько способов настройки вашего приложения в интеграционных тестах, но по своей сути он предоставляет способ запуска экземпляра хоста вашего приложения в памяти. Один из основных методов в этом процессе — EnsureServer(). Он отвечает за создание тестового сервера, предварительно получая экземпляр IHostBuilder. IHostBuilder он пытается получить через Program.CreateHostBuilder(), обычно используемый в ASP.NET Core 3.x/5. Если это не удаётся, он ищет метод Program.CreateWebHostBuilder(), используемый в ASP.NET Core 2.x. Если и это не удается, он прибегает к подходу .NET 6.

В этом подходе используется новый тип DeferredHostBuilder. DeferredHostBuilder предназначен для «захвата» вызываемых в нём методов конфигурации (например, ConfigureServices()), а затем «воспроизведения» их для IHostBuilder реального приложения, как только он станет доступен. Методы «отсрочки» собирают методы конфигурации в виде мультикаст-делегата, а делегаты затем применяются к IHostBuilder, когда вызывается событие HostBuilding экземляра DiagnosticSource.

Затем запускается процесс, описанный в предыдущем посте, в котором приложение выполняется в отдельном потоке, с помощью событий DiagnosticSource вызывается настройка ConfigureHostBuilder() и возвращается экземпляр IHost. При этом приложение в отдельном потоке не перестаёт работать, потому что нам нужно, чтобы остальной код в Program.cs приложения выполнился. DeferredHostBuilder сохраняет IHost в новый тип DefferedHost.

DeferredHost отвечает за ожидание правильного запуска приложения. Ему нужно подождать, пока будут настроены все конечные точки, а также исполнится любой дополнительный стартовый код. Это достигается через существующие события IHostApplicationLifetime, которые вызываются в обычном приложении на универсальном хосте при запуске. В частности, вызов NotifyStarted() вызывает событие ApplicationStarted, которое DeferredHost использует для определения того, что приложение запущено, и можно безопасно запускать тесты. См. рисунок ниже.

После этого WebApplicationFactory создаёт HttpClient, как и в предыдущих версиях, и вы можете выполнять вызовы к приложению в памяти, как и раньше. Стоит знать, что (в отличие от предыдущих версий ASP.NET Core) в ваших тестах будет исполнено всё, что содержится в Program.cs приложения. Но, помимо этого нюанса, всё в вашем тестовом коде останется прежним.

Подробный перевод статьи с примерами кода размещён на Хабре.

Источник: https://andrewlock.net/exploring-dotnet-6-part-4-building-a-middleware-pipeline-with-webapplication/
👍4