diff --git a/docs/app-host/eventing.md b/docs/app-host/eventing.md index 9b0f4ed026..827c6cc352 100644 --- a/docs/app-host/eventing.md +++ b/docs/app-host/eventing.md @@ -26,20 +26,21 @@ All of the preceding events are analogous to the [app host life cycles](xref:dot To subscribe to the built-in app host events, use the eventing API. After you have a distributed application builder instance, walk up to the property and call the API. Consider the following sample app host _Program.cs_ file: -:::code source="snippets/AspireApp/AspireApp.AppHost/Program.cs" highlight="17-25,27-35,37-45"::: +:::code source="snippets/AspireApp/AspireApp.AppHost/Program.cs"::: The preceding code is based on the starter template with the addition of the calls to the `Subscribe` API. The `Subscribe` API returns a instance that you can use to unsubscribe from the event. It's common to discard the returned subscriptions, as you don't usually need to unsubscribe from events as the entire app is torn down when the app host is shut down. When the app host is run, by the time the .NET Aspire dashboard is displayed, you should see the following log output in the console: -:::code language="Plaintext" source="snippets/AspireApp/AspireApp.AppHost/Console.txt" highlight="2,10,16"::: +:::code language="Plaintext" source="snippets/AspireApp/AspireApp.AppHost/Console.txt" highlight="2,10-14,20"::: -The log output confirms that event handlers are executed in the order of the app host life cycle events. The subscription order doesn't affect execution order. The `BeforeStartEvent` is triggered first, followed by `AfterEndpointsAllocatedEvent`, and finally `AfterResourcesCreatedEvent`. +The log output confirms that event handlers are executed in the order of the app host life cycle events. The subscription order doesn't affect execution order. The `BeforeStartEvent` is triggered first, followed by `AfterEndpointsAllocatedEvent`, then each resource has its `ResourceEndpointsAllocatedEvent` event fired, and finally `AfterResourcesCreatedEvent`. ## Resource eventing In addition to the app host events, you can also subscribe to resource events. Resource events are raised specific to an individual resource. Resource events are defined as implementations of the interface. The following resource events are available in the listed order: +1. `InitializeResourceEvent`: Raised by orchestrators to signal to resources that they should initialize themselves. 1. : Raised when a connection string becomes available for a resource. 1. : Raised before the orchestrator starts a new resource. 1. : Raised when a resource initially transitions to a ready state. @@ -48,13 +49,13 @@ In addition to the app host events, you can also subscribe to resource events. R To subscribe to resource events, use the eventing API. After you have a distributed application builder instance, walk up to the property and call the API. Consider the following sample app host _Program.cs_ file: -:::code source="snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs" highlight="8-17,19-28,30-39"::: +:::code source="snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs"::: -The preceding code subscribes to the `ResourceReadyEvent`, `ConnectionStringAvailableEvent`, and `BeforeResourceStartedEvent` events on the `cache` resource. When is called, it returns an where `T` is a . The resource builder exposes the resource as the property. The resource in question is then passed to the `Subscribe` API to subscribe to the events on the resource. +The preceding code subscribes to the `InitializeResourceEvent`, `ResourceReadyEvent`, `ConnectionStringAvailableEvent`, and `BeforeResourceStartedEvent` events on the `cache` resource. When is called, it returns an where `T` is a . The resource builder exposes the resource as the property. The resource in question is then passed to the `Subscribe` API to subscribe to the events on the resource. When the app host is run, by the time the .NET Aspire dashboard is displayed, you should see the following log output in the console: -:::code language="Plaintext" source="snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt" highlight="8,10,12"::: +:::code language="Plaintext" source="snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt" highlight="8,10,12,18"::: > [!NOTE] > Some events are blocking. For example, when the `BeforeResourceStartEvent` is published, the startup of the resource will be blocked until all subscriptions for that event on a given resource have completed executing. Whether an event is blocking or not depends on how it is published (see the following section). @@ -105,28 +106,9 @@ The preceding code: When this app host is run, the life cycle hook is executed for each event. The following output is generated: -```Output -info: LifecycleLogger[0] - BeforeStartAsync -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: LifecycleLogger[0] - 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=d80f598bc8a64c7ee97328a1cbd55d72 -info: LifecycleLogger[0] - AfterResourcesCreatedAsync -info: Aspire.Hosting.DistributedApplication[0] - Distributed application started. Press Ctrl+C to shut down. -``` - -The preferred way to hook into the app host life cycle is to use the eventing API. For more information, see [Eventing in .NET Aspire](#eventing-in-net-aspire). +:::code language="Plaintext" source="../fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Console.txt" highlight="2,10,16"::: + +The preferred way to hook into the app host life cycle is to use the eventing API. For more information, see [App host eventing](#app-host-eventing). ## See also diff --git a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj index d4552282ef..44f2cf540b 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj +++ b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Console.txt b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Console.txt index fe73c4232e..a3eadd2048 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Console.txt +++ b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Console.txt @@ -1,18 +1,22 @@ info: Program[0] 1. BeforeStartEvent info: Aspire.Hosting.DistributedApplication[0] - Aspire version: 9.0.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 + 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= info: Program[0] - 3. AfterResourcesCreatedEvent + 4. AfterResourcesCreatedEvent info: Aspire.Hosting.DistributedApplication[0] - Distributed application started. Press Ctrl+C to shut down. \ No newline at end of file + Distributed application started. Press Ctrl+C to shut down. diff --git a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs index 14af8aa466..125ee9ba4b 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs +++ b/docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs @@ -14,6 +14,17 @@ .WithReference(apiService) .WaitFor(apiService); +builder.Eventing.Subscribe( + (@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( static (@event, cancellationToken) => { @@ -39,7 +50,7 @@ { var logger = @event.Services.GetRequiredService>(); - logger.LogInformation("3. AfterResourcesCreatedEvent"); + logger.LogInformation("4. AfterResourcesCreatedEvent"); return Task.CompletedTask; }); diff --git a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AspireApp.ResourceAppHost.csproj b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AspireApp.ResourceAppHost.csproj index d4552282ef..44f2cf540b 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AspireApp.ResourceAppHost.csproj +++ b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AspireApp.ResourceAppHost.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt index c8ca1f88cb..cdfecf5a0b 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt +++ b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt @@ -1,18 +1,20 @@ info: Aspire.Hosting.DistributedApplication[0] - Aspire version: 9.0.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 + Application host directory is: ../AspireApp/AspireApp.AppHost info: Program[0] - 1. ConnectionStringAvailableEvent + 1. InitializeResourceEvent info: Program[0] - 2. BeforeResourceStartedEvent + 2. ConnectionStringAvailableEvent info: Program[0] - 3. ResourceReadyEvent + 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= +info: Program[0] + 4. ResourceReadyEvent info: Aspire.Hosting.DistributedApplication[0] Distributed application started. Press Ctrl+C to shut down. \ No newline at end of file diff --git a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs index b2134aa607..0350aa75b3 100644 --- a/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs +++ b/docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs @@ -11,7 +11,17 @@ { var logger = @event.Services.GetRequiredService>(); - logger.LogInformation("3. ResourceReadyEvent"); + logger.LogInformation("4. ResourceReadyEvent"); + + return Task.CompletedTask; + }); + +builder.Eventing.Subscribe(cache.Resource, + static (@event, cancellationToken) => + { + var logger = @event.Services.GetRequiredService>(); + + logger.LogInformation("1. InitializeResourceEvent"); return Task.CompletedTask; }); @@ -22,7 +32,7 @@ { var logger = @event.Services.GetRequiredService>(); - logger.LogInformation("2. BeforeResourceStartedEvent"); + logger.LogInformation("3. BeforeResourceStartedEvent"); return Task.CompletedTask; }); @@ -33,7 +43,7 @@ { var logger = @event.Services.GetRequiredService>(); - logger.LogInformation("1. ConnectionStringAvailableEvent"); + logger.LogInformation("2. ConnectionStringAvailableEvent"); return Task.CompletedTask; }); diff --git a/docs/authentication/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj b/docs/authentication/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj index ecf2b62c33..5c7c5a1168 100644 --- a/docs/authentication/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj +++ b/docs/authentication/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -17,7 +17,7 @@ - diff --git a/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Console.txt b/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Console.txt new file mode 100644 index 0000000000..8e36e37c71 --- /dev/null +++ b/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Console.txt @@ -0,0 +1,18 @@ +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= +info: LifecycleLogger[0] + 3. AfterResourcesCreatedAsync +info: Aspire.Hosting.DistributedApplication[0] + Distributed application started. Press Ctrl+C to shut down. \ No newline at end of file diff --git a/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Program.cs b/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Program.cs index 7e9c4fb3e8..5c8b5e3d7e 100644 --- a/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Program.cs +++ b/docs/fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Program.cs @@ -13,21 +13,21 @@ internal sealed class LifecycleLogger(ILogger logger) public Task BeforeStartAsync( DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { - logger.LogInformation("BeforeStartAsync"); + logger.LogInformation("1. BeforeStartAsync"); return Task.CompletedTask; } public Task AfterEndpointsAllocatedAsync( DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { - logger.LogInformation("AfterEndpointsAllocatedAsync"); + logger.LogInformation("2. AfterEndpointsAllocatedAsync"); return Task.CompletedTask; } public Task AfterResourcesCreatedAsync( DistributedApplicationModel appModel, CancellationToken cancellationToken = default) { - logger.LogInformation("AfterResourcesCreatedAsync"); + logger.LogInformation("3. AfterResourcesCreatedAsync"); return Task.CompletedTask; } }