.NET .NET Aspire 中的事件

在 .NET.NET Aspire中,事件处理功能允许你在各种 应用程序主机生命周期期间发布和订阅事件。 事件比生命周期事件更灵活。 这两者都允许在事件回调期间运行任意代码,但事件驱动可以更细致地控制事件的计时和发布,并提供对自定义事件的支持。

.NET .NET Aspire 中的事件机制是 📦Aspire的一部分。托管 NuGet 包。 此包在 Aspire.Hosting.Eventing 命名空间中提供了一组接口和类,用于发布和订阅 .NET.NET Aspire 应用主机项目中的事件。 事件管理的范围限定在应用主机及其内部资源之内。

本文介绍如何在 .NET.NET Aspire中使用事件功能。

应用主机事件机制

以下事件在应用主机中可用,并按以下顺序发生:

  1. BeforeStartEvent:在应用主机启动之前引发此事件。
  2. AfterEndpointsAllocatedEvent:在应用主机分配终结点后引发此事件。
  3. AfterResourcesCreatedEvent:在应用主机创建资源后引发此事件。

上述所有事件都类似于 应用主机生命周期。 也就是说,IDistributedApplicationLifecycleHook 的实现可以同样地处理这些事件。 但是,使用事件 API,可以在触发这些事件时运行任意代码,甚至定义自定义事件——任何实现 IDistributedApplicationEvent 接口的事件。

订阅应用主机事件

若要订阅内置应用宿主事件,请使用事件 API。 获得分布式应用程序生成器实例后,请继续访问 IDistributedApplicationBuilder.Eventing 属性并调用 Subscribe<T>(Func<T,CancellationToken,Task>) API。 请考虑以下示例应用主机 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();

上述代码基于初学者模板,并添加了对 Subscribe API 的调用。 Subscribe<T> API 返回可用于取消订阅事件的 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 接口的实现。 以下资源事件按列出的顺序可用:

  1. ConnectionStringAvailableEvent:当连接字符串可用于资源时触发。
  2. BeforeResourceStartedEvent:在业务流程协调程序启动新资源之前引发。
  3. ResourceReadyEvent:当资源最初转换为就绪状态时触发。

订阅资源事件

若要订阅资源事件,请使用事件 API。 获得分布式应用程序生成器实例后,请继续访问 IDistributedApplicationBuilder.Eventing 属性并调用 Subscribe<T>(IResource, Func<T,CancellationToken,Task>) API。 请考虑以下示例应用主机 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();

前面的代码订阅 cache 资源上的 ResourceReadyEventConnectionStringAvailableEventBeforeResourceStartedEvent 事件。 调用 AddRedis 时,它将返回一个 IResourceBuilder<T>,其中 TRedisResource。 资源生成器将资源作为 IResourceBuilder<T>.Resource 属性进行公开。 然后,将该资源传递给 Subscribe API,以订阅该资源上的事件。

运行应用主机时,在显示 .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 发布自定义事件。 若要发布事件,必须首先将事件定义为 IDistributedApplicationEventIDistributedApplicationResourceEvent 接口的实现。 需要根据事件是全局应用主机事件还是特定于资源的事件来确定要实现的接口。

然后,可以通过调用以下任一 API 来订阅和发布事件:

提供编号EventDispatchBehavior

事件被发送时,您可以控制事件如何发送给订阅者。 使用 EventDispatchBehavior 枚举来定义事件调度行为。 有以下行为可用:

默认行为为 EventDispatchBehavior.BlockingSequential。 若要覆盖此行为,在调用发布 API(如 PublishAsync)时,请将期望的行为作为参数提供。