Skip to content

Extension methods on IDistributedApplicationBuilder for subscribing to application level events.#14119

Open
afscrome wants to merge 4 commits intodotnet:mainfrom
afscrome:application-event-helpers
Open

Extension methods on IDistributedApplicationBuilder for subscribing to application level events.#14119
afscrome wants to merge 4 commits intodotnet:mainfrom
afscrome:application-event-helpers

Conversation

@afscrome
Copy link
Contributor

@afscrome afscrome commented Jan 25, 2026

Description

Moved over what consumers I (copilot really) could to the new helper - the main ones that couldn't be migrated were implementations of IDistributedApplicationEventingSubscriber, as they only get access to IDistributedApplicationEventing, and not the IDistributedApplication these extension method are added to. Since IDistributedApplicationEventingSubscriber is a bit of an advanced case, it doesn't seem unreasonable that they don't get the easy helper method.

I left out AfterEndpointsAllocatedEvent from the new helper methods since it is obsolete.

This is somewhat of a follow on to #10097, but for app host level events.

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

@afscrome afscrome requested a review from mitchdenny as a code owner January 25, 2026 15:48
Copilot AI review requested due to automatic review settings January 25, 2026 15:48
@afscrome afscrome requested a review from jfversluis as a code owner January 25, 2026 15:48
@github-actions
Copy link
Contributor

github-actions bot commented Jan 25, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14119

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14119"

@afscrome afscrome changed the title Introduce extension methods on IDistributedApplicationBuilder for subscribing to application level events. Extension methods on IDistributedApplicationBuilder for subscribing to application level events. Jan 25, 2026
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Jan 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces convenience extension methods on IDistributedApplicationBuilder for subscribing to application-level events, following up on PR #10097 which added similar methods for resource-level events. The PR adds four new extension methods (OnBeforeStart, OnAfterResourcesCreated, OnBeforePublish, OnAfterPublish) that wrap the underlying builder.Eventing.Subscribe<T> calls with a more fluent and discoverable API. Existing code across multiple files has been migrated to use these new helper methods.

Changes:

  • Added four new public extension methods on IDistributedApplicationBuilder for subscribing to application events
  • Refactored existing OnEvent method to OnResourceEvent for clarity
  • Migrated 14+ call sites from builder.Eventing.Subscribe<Event> to the new builder.OnEvent() syntax

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/Aspire.Hosting/DistributedApplicationEventingExtensions.cs Core implementation adding OnBeforeStart, OnAfterResourcesCreated, OnBeforePublish, OnAfterPublish methods and refactoring existing resource event methods
tests/Aspire.Hosting.Tests/OperationModesTests.cs Updated test to use new OnAfterResourcesCreated method
src/Aspire.Hosting/ContainerRegistryResourceBuilderExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Yarp/YarpResourceExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Redis/RedisBuilderExtensions.cs Migrated two BeforeStartEvent subscriptions to OnBeforeStart
src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs Migrated three BeforeStartEvent subscriptions to OnBeforeStart
src/Aspire.Hosting.Maui/MauiOtlpExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Keycloak/KeycloakResourceBuilderExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.JavaScript/JavaScriptHostingExtensions.cs CRITICAL: Contains corrupted code with malformed method bodies that will not compile
src/Aspire.Hosting.Azure.Redis/AzureRedisExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Azure.Redis/AzureManagedRedisExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Azure.PostgreSQL/AzurePostgresExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Azure.Functions/AzureFunctionsProjectResourceExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
src/Aspire.Hosting.Azure.ContainerRegistry/AzureContainerRegistryExtensions.cs Migrated BeforeStartEvent subscription to OnBeforeStart
playground/DotnetTool/DotnetTool.AppHost/AppHost.cs Migrated BeforeStartEvent subscription to OnBeforeStart

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 15 to 57
/// <summary>
/// Subscribes a callback to the <see cref="BeforeStartEvent"/> event within the AppHost.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IDistributedApplicationBuilder"/>.</returns>
/// <remarks>If you need to ensure you only subscribe to the event once, see <see cref="Lifecycle.IDistributedApplicationEventingSubscriber"/>.</remarks>
public static T OnBeforeStart<T>(this T builder, Func<BeforeStartEvent, CancellationToken, Task> callback)
where T : IDistributedApplicationBuilder
=> builder.OnApplicationEvent(callback);

/// <summary>
/// Subscribes a callback to the <see cref="AfterResourcesCreatedEvent"/> event within the AppHost.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IDistributedApplicationBuilder"/>.</returns>
/// <remarks>If you need to ensure you only subscribe to the event once, see <see cref="Lifecycle.IDistributedApplicationEventingSubscriber"/>.</remarks>
public static T OnAfterResourcesCreated<T>(this T builder, Func<AfterResourcesCreatedEvent, CancellationToken, Task> callback)
where T : IDistributedApplicationBuilder
=> builder.OnApplicationEvent(callback);

/// <summary>
/// Subscribes a callback to the <see cref="BeforePublishEvent"/> event within the AppHost.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IDistributedApplicationBuilder"/>.</returns>
/// <remarks>If you need to ensure you only subscribe to the event once, see <see cref="Lifecycle.IDistributedApplicationEventingSubscriber"/>.</remarks>
public static T OnBeforePublish<T>(this T builder, Func<BeforePublishEvent, CancellationToken, Task> callback)
where T : IDistributedApplicationBuilder
=> builder.OnApplicationEvent(callback);

/// <summary>
/// Subscribes a callback to the <see cref="AfterPublishEvent"/> event within the AppHost.
/// </summary>
/// <param name="builder">The distributed application builder.</param>
/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IDistributedApplicationBuilder"/>.</returns>
/// <remarks>If you need to ensure you only subscribe to the event once, see <see cref="Lifecycle.IDistributedApplicationEventingSubscriber"/>.</remarks>
public static T OnAfterPublish<T>(this T builder, Func<AfterPublishEvent, CancellationToken, Task> callback)
where T : IDistributedApplicationBuilder
=> builder.OnApplicationEvent(callback);
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding code examples to these new public API methods to demonstrate typical usage patterns. While the related resource event methods from PR #10097 also lack examples, providing them would improve developer experience. For instance, showing how to use OnBeforeStart to access services and modify the application model before startup would be valuable. The XML documentation coding guidelines recommend examples for public APIs, especially for new extension methods.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…ubscribing to application level events.

This is similar to dotnet#10097, but for app host level events.

Moved over what consumers I (copilot really) could to the new helper - the main ones that couldn't be migrated were implementations of  `IDistributedApplicationEventingSubscriber`, as they only get access to `IDistributedApplicationEventing`, and not the `IDistributedApplication` these extension method are added to.  Since `IDistributedApplicationEventingSubscriber` is a bit of an advanced case, it doesn't seem unreasonable that they don't get the easy helper method.

I left out `AfterEndpointsAllocatedEvent` from the new helper methods since it is obsolete.
@afscrome afscrome force-pushed the application-event-helpers branch from 8bdadd2 to a050ca7 Compare January 25, 2026 16:28
@davidfowl
Copy link
Member

I think we want to export these methods using the new [AspireExport] attribute. Though I wonder if the generics will be a problem

/// <returns>The <paramref name="builder"/> for chaining.</returns>
/// <remarks>If you need to ensure you only subscribe to the event once, see <see cref="Lifecycle.IDistributedApplicationEventingSubscriber"/>.</remarks>
[AspireExport("onAfterResourcesCreated", Description = "Subscribes a callback to the AfterResourcesCreatedEvent event within the AppHost.")]
public static T OnAfterResourcesCreated<T>(this T builder, Func<AfterResourcesCreatedEvent, CancellationToken, Task> callback)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you still planning on deprecating this event? In which case, does it make sense to not implement this helper / export it with AspireExport?

#7009 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so.

/// <param name="callback">A callback to handle the event.</param>
/// <returns>The <see cref="IResourceBuilder{T}"/>.</returns>
/// <returns>The <paramref name="builder"/> for chaining.</returns>
[AspireExport("onInitializeResource", Description = "Subscribes a callback to the InitializeResourceEvent event of the resource.")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to expose this event with AspireExport?. This event was introduced to allow custom resources to initialise themselves - does that level of initialisation make sense outside the app host itself?

@mitchdenny
Copy link
Member

Thanks for this contribution @afscrome! This is a nice API ergonomics improvement that makes event subscriptions more discoverable and consistent with the existing resource-level patterns.

We'd like to take this change. Before we can merge, we just need a couple of things sorted out:

  1. Test coverage - Could you add some basic tests for the new public API methods (OnBeforeStart, OnAfterResourcesCreated, OnBeforePublish, OnAfterPublish)? A simple test verifying each method correctly subscribes to its respective event would be sufficient.

  2. Docs issue - The checklist indicates docs updates are needed. Could you file an issue in dotnet/docs-aspire and link it here?

Also a minor fix needed: there's a missing newline at the end of JavaScriptHostingExtensions.cs.

Thanks again for the contribution!

- Add tests for OnBeforeStart, OnAfterResourcesCreated, OnBeforePublish, OnAfterPublish
- Add tests verifying builder is returned for method chaining
- Fix missing newline at EOF in JavaScriptHostingExtensions.cs
@mitchdenny
Copy link
Member

I've added basic test coverage for the new application-level event helper methods in commit 6e475dc:

  • Tests for OnBeforeStart, OnAfterResourcesCreated, OnBeforePublish, OnAfterPublish verifying event subscription works
  • Tests verifying the builder is returned for method chaining
  • Fixed the missing newline at EOF in JavaScriptHostingExtensions.cs

The docs-aspire issue is still needed before we can merge.

@IEvangelist
Copy link
Member

2. Docs issue - The checklist indicates docs updates are needed. Could you file an issue in dotnet/docs-aspire and link it here?

Docs issues should be filed in microsoft/aspire.dev.

@afscrome
Copy link
Contributor Author

afscrome commented Jan 29, 2026

Docs issues should be filed in microsoft/aspire.dev.

#13149 hint hint @IEvangelist

@afscrome
Copy link
Contributor Author

Docs issue: microsoft/aspire.dev#315

@IEvangelist
Copy link
Member

Docs issues should be filed in microsoft/aspire.dev.

#13149 hint hint @IEvangelist

Yeah, fair callout. We're trying to automate that to remove the template altogether.

@davidfowl
Copy link
Member

@IEvangelist CI failing on the banner test

@davidfowl davidfowl closed this Feb 3, 2026
@davidfowl davidfowl reopened this Feb 3, 2026
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants