События в .NET.NET Aspire
В .NET.NET Aspireвзаимодействие с событиями позволяет публиковать и подписываться на события в течение различных циклов жизни хоста приложения . Организация событий более гибкая, чем события жизненного цикла. Обе системы позволяют выполнять произвольный код во время обратных вызовов событий, но eventing обеспечивает более точное управление временем выполнения событий, их публикацией и поддерживает создание пользовательских событий.
Механизмы событий в .NET.NET Aspire являются частью 📦Aspire. Размещение пакета NuGet. Этот пакет предоставляет набор интерфейсов и классов в пространстве имен Aspire.Hosting.Eventing, которые вы используете для публикации и подписки на события в проекте узла приложения .NET.NET Aspire. Обработка событий ограничивается только узлом приложения и его внутренними ресурсами.
В этой статье вы узнаете, как использовать функции событий в .NET.NET Aspire.
Событие хоста приложения
Следующие события доступны в узле приложения и выполняются в следующем порядке:
- BeforeStartEvent: Это событие возникает перед запуском хоста приложения.
- AfterEndpointsAllocatedEvent: Это событие возникает после того, как хост приложения выделил конечные точки.
- AfterResourcesCreatedEvent: Это событие возникает после того, как узлом приложения созданы ресурсы.
Все предыдущие события аналогичны жизненным циклам хоста приложения . То есть реализация IDistributedApplicationLifecycleHook может обрабатывать эти события так же. Однако с помощью API событий вы можете запустить произвольный код, когда эти события создаются, и даже определить пользовательские события — любое событие, реализующее интерфейс IDistributedApplicationEvent.
Подписка на события хоста приложения
Чтобы подписаться на встроенные события узла приложения, используйте API событий. Получив экземпляр распределенного построителя приложений, перейдите к свойству IDistributedApplicationBuilder.Eventing и вызовите API Subscribe<T>(Func<T,CancellationToken,Task>). Рассмотрим следующий пример файла Program.cs узла приложения:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(cache)
.WaitFor(cache)
.WithReference(apiService)
.WaitFor(apiService);
builder.Eventing.Subscribe<BeforeStartEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("1. BeforeStartEvent");
return Task.CompletedTask;
});
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("2. AfterEndpointsAllocatedEvent");
return Task.CompletedTask;
});
builder.Eventing.Subscribe<AfterResourcesCreatedEvent>(
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("3. AfterResourcesCreatedEvent");
return Task.CompletedTask;
});
builder.Build().Run();
Предыдущий код основан на начальном шаблоне с добавлением вызовов к API Subscribe
. API Subscribe<T>
возвращает экземпляр DistributedApplicationEventSubscription, который можно использовать для отмены подписки от события. Обычно отказываются от возвращаемых подписок, поскольку нет необходимости отменять подписки на события, так как все приложение отключается при завершении работы хоста приложения.
При запуске хоста приложения к моменту отображения панели мониторинга .NET.NET Aspire вы должны увидеть следующие журнальные данные в консоли:
info: Program[0]
1. BeforeStartEvent
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 9.0.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Application host directory is: ..\AspireApp\AspireApp.AppHost
info: Program[0]
2. AfterEndpointsAllocatedEvent
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17178
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at https://localhost:17178/login?t=<YOUR_TOKEN>
info: Program[0]
3. AfterResourcesCreatedEvent
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
Выходные данные журнала подтверждают, что обработчики событий выполняются в порядке событий жизненного цикла узла приложения. Порядок подписки не влияет на последовательность выполнения. Сначала запускается BeforeStartEvent
, а затем AfterEndpointsAllocatedEvent
и, наконец, AfterResourcesCreatedEvent
.
Управление событиями ресурсов
Помимо событий узла приложения, вы также можете подписаться на ресурсные события. События ресурсов специфично возникают для отдельного ресурса. События ресурсов определяются как реализации интерфейса IDistributedApplicationResourceEvent. Следующие события ресурсов доступны в указанном порядке:
- ConnectionStringAvailableEvent: Выдается сообщение о том, что строка подключения стала доступной для ресурса.
- BeforeResourceStartedEvent. Вызывается перед запуском нового ресурса оркестратора.
- ResourceReadyEvent: возникает при первоначальном переходе ресурса в готовое состояние.
Подписка на события ресурсов
Чтобы подписаться на события ресурсов, используйте API событий. Получив экземпляр распределенного построителя приложений, перейдите к свойству IDistributedApplicationBuilder.Eventing и вызовите API Subscribe<T>(IResource, Func<T,CancellationToken,Task>). Рассмотрим следующий пример файла Program.cs узла приложения:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
builder.Eventing.Subscribe<ResourceReadyEvent>(
cache.Resource,
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("3. ResourceReadyEvent");
return Task.CompletedTask;
});
builder.Eventing.Subscribe<BeforeResourceStartedEvent>(
cache.Resource,
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("2. BeforeResourceStartedEvent");
return Task.CompletedTask;
});
builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(
cache.Resource,
static (@event, cancellationToken) =>
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("1. ConnectionStringAvailableEvent");
return Task.CompletedTask;
});
var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(cache)
.WaitFor(cache)
.WithReference(apiService)
.WaitFor(apiService);
builder.Build().Run();
Код, приведенный выше, подписывается на события ResourceReadyEvent
, ConnectionStringAvailableEvent
и BeforeResourceStartedEvent
в ресурсе cache
. При вызове AddRedis возвращается IResourceBuilder<T>, где T
является RedisResource. Построитель ресурсов предоставляет ресурс в качестве свойства IResourceBuilder<T>.Resource. Затем указанный ресурс передается в API Subscribe
, чтобы подписаться на события ресурса.
При выполнении хоста приложения к моменту отображения панели мониторинга .NET.NET Aspire в консоли отобразится следующая запись журнала:
info: Aspire.Hosting.DistributedApplication[0]
Aspire version: 9.0.0
info: Aspire.Hosting.DistributedApplication[0]
Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
Application host directory is: ..\AspireApp\AspireApp.AppHost
info: Program[0]
1. ConnectionStringAvailableEvent
info: Program[0]
2. BeforeResourceStartedEvent
info: Program[0]
3. ResourceReadyEvent
info: Aspire.Hosting.DistributedApplication[0]
Now listening on: https://localhost:17222
info: Aspire.Hosting.DistributedApplication[0]
Login to the dashboard at https://localhost:17222/login?t=<YOUR_TOKEN>
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
Заметка
Некоторые события блокируются. Например, при публикации BeforeResourceStartEvent
запуск ресурса будет заблокирован до завершения выполнения всех подписок на данное событие. Указывает, блокируется ли событие или не зависит от того, как оно опубликовано (см. следующий раздел).
Публикация событий
При подписке на любые встроенные события вам не нужно публиковать событие самостоятельно, так как оркестратор узла приложений управляет публикацией встроенных событий от вашего имени. Однако можно опубликовать пользовательские события с помощью API событий. Чтобы опубликовать событие, сначала необходимо определить событие как реализацию интерфейса IDistributedApplicationEvent или IDistributedApplicationResourceEvent. Необходимо определить, какой интерфейс следует реализовать на основе того, является ли событие глобальным событием хоста приложения или событием, специфичным для ресурса.
Затем вы можете подписаться и опубликовать событие, вызвав любой из следующих API:
- PublishAsync<T>(T, CancellationToken): публикует событие для всех подписчиков определенного типа события.
- PublishAsync<T>(T, EventDispatchBehavior, CancellationToken): публикует событие всем подписчикам определённого типа события с заданным образом отправки.
Предоставьте EventDispatchBehavior
При отправке событий можно управлять отправкой событий подписчикам. Поведение диспетчеризации событий указывается в перечислении EventDispatchBehavior
. Доступны следующие действия:
- EventDispatchBehavior.BlockingSequential: последовательно запускает события и блокируется, пока все не будут обработаны.
- EventDispatchBehavior.BlockingConcurrent: одновременно запускает события и блоки, пока они не будут обработаны.
- EventDispatchBehavior.NonBlockingSequential: инициирует события последовательно, но не блокирует.
- EventDispatchBehavior.NonBlockingConcurrent: одновременно запускает события, но не блокирует выполнение.
Поведение по умолчанию — EventDispatchBehavior.BlockingSequential
. Чтобы переопределить это поведение, при вызове API публикации, например PublishAsync, укажите требуемое поведение в качестве аргумента.
.NET Aspire