Skip to content

✅ Merge main into live #3506

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@
{
"source_path_from_root": "/docs/deployment/azure/local-provisioning.md",
"redirect_url": "/dotnet/aspire/azure/local-provisioning"
},
{
"source_path_from_root": "/docs/frameworks/dapr.md",
"redirect_url": "/dotnet/aspire/azure/dapr"
}
]
}
2 changes: 2 additions & 0 deletions docs/app-host/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ By default, the dashboard is automatically started by the app host. The dashboar
| `ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL` | `null` | Configures the dashboard OTLP HTTP address. Used by the dashboard to receive telemetry over OTLP. If only `ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL` is configured then it is set on resources as the `OTEL_EXPORTER_OTLP_ENDPOINT` env var. The `OTEL_EXPORTER_OTLP_PROTOCOL` env var is `http/protobuf`. |
| `ASPIRE_DASHBOARD_CORS_ALLOWED_ORIGINS` | `null` | Overrides the CORS allowed origins configured in the dashboard. This setting replaces the default behavior of calculating allowed origins based on resource endpoints. |
| `ASPIRE_DASHBOARD_FRONTEND_BROWSERTOKEN` | Automatically generated 128-bit entropy token. | Configures the frontend browser token. This is the value that must be entered to access the dashboard when the auth mode is BrowserToken. If no browser token is specified then a new token is generated each time the app host is launched. |
| `ASPIRE_DASHBOARD_TELEMETRY_OPTOUT` | `false` | Configures the dashboard to never send [usage telemetry](../fundamentals/dashboard/microsoft-collected-dashboard-telemetry.md). |
| `ASPIRE_DASHBOARD_AI_DISABLED` | `false` | [GitHub Copilot in the dashboard](../fundamentals/dashboard/copilot.md) is available when the app host is launched by a supported IDE. When set to `true` Copilot is disabled in the dashboard and no Copilot UI is visible. |

## Internal

Expand Down
38 changes: 10 additions & 28 deletions docs/app-host/eventing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <xref:Aspire.Hosting.IDistributedApplicationBuilder.Eventing?displayProperty=nameWithType> property and call the <xref:Aspire.Hosting.Eventing.IDistributedApplicationEventing.Subscribe``1(System.Func{``0,System.Threading.CancellationToken,System.Threading.Tasks.Task})> 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<T>` API returns a <xref:Aspire.Hosting.Eventing.DistributedApplicationEventSubscription> 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 <xref:Aspire.Hosting.Eventing.IDistributedApplicationResourceEvent> 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. <xref:Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent>: Raised when a connection string becomes available for a resource.
1. <xref:Aspire.Hosting.ApplicationModel.BeforeResourceStartedEvent>: Raised before the orchestrator starts a new resource.
1. <xref:Aspire.Hosting.ApplicationModel.ResourceReadyEvent>: Raised when a resource initially transitions to a ready state.
Expand All @@ -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 <xref:Aspire.Hosting.IDistributedApplicationBuilder.Eventing?displayProperty=nameWithType> property and call the <xref:Aspire.Hosting.Eventing.IDistributedApplicationEventing.Subscribe``1(Aspire.Hosting.ApplicationModel.IResource,System.Func{``0,System.Threading.CancellationToken,System.Threading.Tasks.Task})> 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 <xref:Aspire.Hosting.RedisBuilderExtensions.AddRedis*> is called, it returns an <xref:Aspire.Hosting.ApplicationModel.IResourceBuilder`1> where `T` is a <xref:Aspire.Hosting.ApplicationModel.RedisResource>. The resource builder exposes the resource as the <xref:Aspire.Hosting.ApplicationModel.IResourceBuilder`1.Resource?displayProperty=nameWithType> 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 <xref:Aspire.Hosting.RedisBuilderExtensions.AddRedis*> is called, it returns an <xref:Aspire.Hosting.ApplicationModel.IResourceBuilder`1> where `T` is a <xref:Aspire.Hosting.ApplicationModel.RedisResource>. The resource builder exposes the resource as the <xref:Aspire.Hosting.ApplicationModel.IResourceBuilder`1.Resource?displayProperty=nameWithType> 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).
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.1" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.2.1" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.0-preview.1.25262.2" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.3.0-preview.1.25262.2" />
</ItemGroup>

</Project>
12 changes: 8 additions & 4 deletions docs/app-host/snippets/AspireApp/AspireApp.AppHost/Console.txt
Original file line number Diff line number Diff line change
@@ -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=<YOUR_TOKEN>
info: Program[0]
3. AfterResourcesCreatedEvent
4. AfterResourcesCreatedEvent
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
Distributed application started. Press Ctrl+C to shut down.
13 changes: 12 additions & 1 deletion docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@
.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) =>
{
Expand All @@ -39,7 +50,7 @@
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();

logger.LogInformation("3. AfterResourcesCreatedEvent");
logger.LogInformation("4. AfterResourcesCreatedEvent");

return Task.CompletedTask;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.2.1" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.2.1" />
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.0-preview.1.25262.2" />
<PackageReference Include="Aspire.Hosting.Redis" Version="9.3.0-preview.1.25262.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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=<YOUR_TOKEN>
info: Program[0]
4. ResourceReadyEvent
info: Aspire.Hosting.DistributedApplication[0]
Distributed application started. Press Ctrl+C to shut down.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,17 @@
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();

logger.LogInformation("3. ResourceReadyEvent");
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;
});
Expand All @@ -22,7 +32,7 @@
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();

logger.LogInformation("2. BeforeResourceStartedEvent");
logger.LogInformation("3. BeforeResourceStartedEvent");

return Task.CompletedTask;
});
Expand All @@ -33,7 +43,7 @@
{
var logger = @event.Services.GetRequiredService<ILogger<Program>>();

logger.LogInformation("1. ConnectionStringAvailableEvent");
logger.LogInformation("2. ConnectionStringAvailableEvent");

return Task.CompletedTask;
});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading