Evento em .NET.NET Aspire
Em .NET.NET Aspire, o evento permite que você publique e assine eventos durante vários ciclos de vida do host do aplicativo. O evento é mais flexível do que os eventos do ciclo de vida. Ambos permitem executar código arbitrário durante os retornos de chamada de evento, mas o sistema de eventos oferece um controle mais preciso sobre o tempo dos eventos, a publicação e fornece suporte para eventos personalizados.
Os mecanismos de eventos em .NET.NET Aspire fazem parte do 📦Aspiredo pacote NuGet de hospedagem. Esse pacote fornece um conjunto de interfaces e classes no namespace Aspire.Hosting.Eventing que você usa para publicar e assinar eventos em seu projeto de host de aplicativo .NET.NET Aspire. O mecanismo de eventos está limitado ao próprio host do aplicativo e aos recursos que ele contém.
Neste artigo, você aprenderá a usar as funcionalidades de eventos em .NET.NET Aspire.
Sistema de eventos do host de aplicativo
Os seguintes eventos estão disponíveis no host do aplicativo e ocorrem na seguinte ordem:
- BeforeStartEvent: esse evento é gerado antes do início do host do aplicativo.
- AfterEndpointsAllocatedEvent: Este evento é gerado após os endpoints do host do aplicativo serem alocados.
- AfterResourcesCreatedEvent: esse evento é gerado após o host do aplicativo criar recursos.
Todos os eventos anteriores são análogos aos ciclos de vida do aplicativo host . Ou seja, uma implementação do IDistributedApplicationLifecycleHook poderia lidar com esses eventos da mesma forma. Com a API de eventos, no entanto, você pode executar código arbitrário quando esses eventos são acionados e até definir eventos personalizados — qualquer evento que implemente a interface IDistributedApplicationEvent.
Assinar eventos de host do aplicativo
Para assinar os eventos internos do host do aplicativo, use a API de eventos. Depois de ter uma instância do construtor de aplicativos distribuída, vá até a propriedade IDistributedApplicationBuilder.Eventing e chame a API Subscribe<T>(Func<T,CancellationToken,Task>). Considere o seguinte arquivo Program.cs de host de aplicativo de exemplo:
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();
O código anterior baseia-se no modelo inicial com a adição das chamadas à API Subscribe
. A API Subscribe<T>
retorna uma instância de DistributedApplicationEventSubscription que você pode usar para cancelar a assinatura do evento. É comum descartar as assinaturas retornadas, pois normalmente você não precisa cancelar a assinatura de eventos, já que todo o aplicativo é encerrado quando o host do aplicativo é desligado.
Ao executar o host do aplicativo, até que o painel .NET.NET Aspire seja exibido, você deverá ver a seguinte saída de log no console:
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.
A saída do log confirma que os manipuladores de eventos são executados na ordem dos eventos do ciclo de vida do host do aplicativo. A ordem da assinatura não afeta a ordem de execução. O BeforeStartEvent
é disparado primeiro, seguido por AfterEndpointsAllocatedEvent
e, por fim, AfterResourcesCreatedEvent
.
Gerenciamento de eventos de recursos
Além dos eventos de host do aplicativo, você também pode assinar eventos de recurso. Os eventos de recurso são gerados especificamente para cada recurso individual. Os eventos de recurso são definidos como implementações da interface IDistributedApplicationResourceEvent. Os seguintes eventos de recurso estão disponíveis na ordem listada:
- ConnectionStringAvailableEvent: Emitido quando uma cadeia de conexão fica disponível para um recurso.
- BeforeResourceStartedEvent: gerado antes que o orquestrador inicie um novo recurso.
- ResourceReadyEvent: gerado quando um recurso faz a transição inicialmente para um estado pronto.
Assinar eventos de recursos
Para assinar eventos de recurso, use a API de eventos. Depois de ter uma instância do construtor de aplicativos distribuída, vá até a propriedade IDistributedApplicationBuilder.Eventing e chame a API Subscribe<T>(IResource, Func<T,CancellationToken,Task>). Considere o arquivo de exemplo de host de aplicativo a seguir, 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();
O código anterior se inscreve nos eventos ResourceReadyEvent
, ConnectionStringAvailableEvent
e BeforeResourceStartedEvent
no recurso cache
. Quando AddRedis é chamado, ele retorna uma IResourceBuilder<T> quando T
é um RedisResource. O construtor de recursos expõe o recurso como a propriedade IResourceBuilder<T>.Resource. Em seguida, o recurso em questão é passado para a API Subscribe
para assinar os eventos no recurso.
No momento em que o host do aplicativo for executado e o painel .NET.NET Aspire for exibido, você deverá ver a seguinte saída de log no console:
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.
Nota
Alguns eventos são bloqueantes. Por exemplo, quando o BeforeResourceStartEvent
for publicado, a inicialização do recurso será bloqueada até que todas as assinaturas para esse evento em um determinado recurso tenham concluído a execução. Se um evento está bloqueando ou não depende de como ele é publicado (consulte a seção a seguir).
Publicar eventos
Ao assinar qualquer um dos eventos internos, você não precisa publicar o evento por conta própria, pois o orquestrador de host do aplicativo consegue publicar eventos internos em seu nome. No entanto, você pode publicar eventos personalizados com a API de eventos. Para publicar um evento, primeiro você precisa definir um evento como uma implementação da interface IDistributedApplicationEvent ou IDistributedApplicationResourceEvent. Você precisa determinar qual interface implementar com base em se o evento é um evento de host de aplicativo global ou um evento específico do recurso.
Em seguida, você pode assinar e publicar o evento chamando qualquer uma das seguintes APIs:
- PublishAsync<T>(T, CancellationToken): Publica um evento para todos os assinantes do tipo de evento específico.
- PublishAsync<T>(T, EventDispatchBehavior, CancellationToken): Publica um evento para todos os assinantes do tipo de evento específico com um comportamento de despacho especificado.
Forneça um EventDispatchBehavior
Quando os eventos são expedidos, você pode controlar como os eventos são enviados aos assinantes. O comportamento de despacho de eventos é especificado com a enumeração EventDispatchBehavior
. Os seguintes comportamentos estão disponíveis:
- EventDispatchBehavior.BlockingSequential: dispara eventos sequencialmente e bloqueia até que todos sejam processados.
- EventDispatchBehavior.BlockingConcurrent: aciona eventos simultaneamente e bloqueia até que todos sejam processados.
- EventDispatchBehavior.NonBlockingSequential: dispara eventos sequencialmente, mas não bloqueia.
- EventDispatchBehavior.NonBlockingConcurrent: aciona eventos simultaneamente, mas não bloqueia.
O comportamento padrão é EventDispatchBehavior.BlockingSequential
. Para substituir esse comportamento, ao chamar uma API de publicação, como PublishAsync, forneça o comportamento desejado como um argumento.