.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<ResourceEndpointsAllocatedEvent>(
    (@event, cancellationToken) =>
    {
        // The event doesn't expose an IServiceProvider, just write to the console.
        Console.WriteLine($"""
                    3. '{@event.Resource.Name}' ResourceEndpointsAllocatedEvent
            """);

        return Task.CompletedTask;
    });

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("4. 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.3.0-preview.1.25262.2+6d54dc081cd2e7ea435e33f7c0e62ff6946ae66d
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
        3. 'aspire-dashboard' ResourceEndpointsAllocatedEvent
        3. 'cache' ResourceEndpointsAllocatedEvent
        3. 'apiservice' ResourceEndpointsAllocatedEvent
        3. 'webfrontend' ResourceEndpointsAllocatedEvent
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]
      4. AfterResourcesCreatedEvent
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application started. Press Ctrl+C to shut down.

日志输出确认事件处理程序按应用主机生命周期事件的顺序执行。 订阅顺序不会影响执行顺序。 首先触发BeforeStartEvent,然后是AfterEndpointsAllocatedEvent,接着每个资源的ResourceEndpointsAllocatedEvent事件被触发,最后是AfterResourcesCreatedEvent

资源事件管理

除了应用主机事件,还可以订阅资源事件。 资源事件是特定于单个资源触发的。 资源事件定义为 IDistributedApplicationResourceEvent 接口的实现。 以下资源事件按列出的顺序可用:

  1. InitializeResourceEvent:由协调器引发,以向资源发出信号,指示它们应自行初始化。
  2. ConnectionStringAvailableEvent:当连接字符串可用于资源时触发。
  3. BeforeResourceStartedEvent:在业务流程协调程序启动新资源之前引发。
  4. 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("4. ResourceReadyEvent");

        return Task.CompletedTask;
    });

builder.Eventing.Subscribe<InitializeResourceEvent>(cache.Resource,
    static (@event, cancellationToken) =>
    {
        var logger = @event.Services.GetRequiredService<ILogger<Program>>();

        logger.LogInformation("1. InitializeResourceEvent");

        return Task.CompletedTask;
    });

builder.Eventing.Subscribe<BeforeResourceStartedEvent>(
    cache.Resource,
    static (@event, cancellationToken) =>
    {
        var logger = @event.Services.GetRequiredService<ILogger<Program>>();

        logger.LogInformation("3. BeforeResourceStartedEvent");

        return Task.CompletedTask;
    });

builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(
    cache.Resource,
    static (@event, cancellationToken) =>
    {
        var logger = @event.Services.GetRequiredService<ILogger<Program>>();

        logger.LogInformation("2. 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资源上的InitializeResourceEventResourceReadyEventConnectionStringAvailableEventBeforeResourceStartedEvent事件。 调用 AddRedis 时,它将返回一个 IResourceBuilder<T>,其中 TRedisResource。 资源生成器将资源作为 IResourceBuilder<T>.Resource 属性进行公开。 然后,将该资源传递给 Subscribe API,以订阅该资源上的事件。

运行应用主机时,在显示 .NET.NET Aspire 仪表板时,控制台中应会显示以下日志输出:

info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 9.3.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. InitializeResourceEvent
info: Program[0]
      2. ConnectionStringAvailableEvent
info: Program[0]
      3. BeforeResourceStartedEvent
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: Program[0]
      4. ResourceReadyEvent
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application started. Press Ctrl+C to shut down.

注意

某些事件是阻碍因素。 例如,当发布 BeforeResourceStartEvent 时,资源的启动将被阻塞,直到给定资源上该事件的所有订阅完成执行。 事件是否被阻止取决于其发布方式(请参阅以下部分)。

发布事件

订阅任何内置事件时,无需自行发布事件,因为应用主机协调程序会代你发布内置事件。 但是,可以使用事件 API 发布自定义事件。 若要发布事件,必须首先将事件定义为 IDistributedApplicationEventIDistributedApplicationResourceEvent 接口的实现。 需要根据事件是全局应用主机事件还是特定于资源的事件来确定要实现的接口。

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

提供编号EventDispatchBehavior

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

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

应用主机生命周期事件

如你所看到的,事件驱动是最灵活的方式。 但是,在本部分中,你将了解替代项:生命周期事件。

.NET .NET Aspire 应用主机公开了几个生命周期,可以通过实现 IDistributedApplicationLifecycleHook 接口来对其进行挂钩。 可以使用以下生命周期方法:

下单 方法 DESCRIPTION
1 BeforeStartAsync 在分布式应用程序启动之前运行。
2 AfterEndpointsAllocatedAsync 在协调器为应用程序模型中的资源分配端点之后运行。
3 AfterResourcesCreatedAsync 在业务流程协调程序创建资源后运行。

注册生命周期钩子

若要注册生命周期挂钩,请实现 IDistributedApplicationLifecycleHook 接口,并使用 AddLifecycleHook API 向应用主机注册挂钩:

using Aspire.Hosting.Lifecycle;
using Microsoft.Extensions.Logging;

var builder = DistributedApplication.CreateBuilder(args);

builder.Services.AddLifecycleHook<LifecycleLogger>();

builder.Build().Run();

internal sealed class LifecycleLogger(ILogger<LifecycleLogger> logger)
    : IDistributedApplicationLifecycleHook
{
    public Task BeforeStartAsync(
        DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
    {
        logger.LogInformation("1. BeforeStartAsync");
        return Task.CompletedTask;
    }

    public Task AfterEndpointsAllocatedAsync(
        DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
    {
        logger.LogInformation("2. AfterEndpointsAllocatedAsync");
        return Task.CompletedTask;
    }

    public Task AfterResourcesCreatedAsync(
        DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
    {
        logger.LogInformation("3. AfterResourcesCreatedAsync");
        return Task.CompletedTask;
    }
}

前面的代码:

运行此应用主机时,将为每个事件执行生命周期钩子。 生成以下输出:

info: LifecycleLogger[0]
      1. BeforeStartAsync
info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 9.3.0
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
      Application host directory is: ../AspireApp/AspireApp.AppHost
info: LifecycleLogger[0]
      2. AfterEndpointsAllocatedAsync
info: Aspire.Hosting.DistributedApplication[0]
      Now listening on: https://localhost:17043
info: Aspire.Hosting.DistributedApplication[0]
      Login to the dashboard at https://localhost:17043/login?t=<YOUR_TOKEN>
info: LifecycleLogger[0]
      3. AfterResourcesCreatedAsync
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application started. Press Ctrl+C to shut down.

连接到应用主机生命周期的首选方法是使用事件 API。 有关详细信息,请参阅 应用主机事件

另请参阅