Skip to content

Commit dd438bc

Browse files
Merge pull request #3926 from dotnet/release-9.4
.NET Aspire release 9.4 content
2 parents 4d015bf + e484188 commit dd438bc

File tree

259 files changed

+7303
-545
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

259 files changed

+7303
-545
lines changed

.github/prompts/adhere-to-writing-style.prompt.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Write for readers who may have a limited vocabulary or are not native English sp
77
- Use active voice
88
- Use the second person (you, your) to address the reader directly
99

10+
## Lists
11+
12+
Use lists more sparingly. Use them only when they help to clarify the content. Use bullet points for unordered lists and numbered lists for ordered lists. Regardless of the type of list, always use sentence-style capitalization, complete sentences, and proper punctuation.
13+
1014
## Headings
1115

1216
Headings use sentence-style capitalization. Always capitalize the first word of a heading. Do not use gerunds (e.g., "Using" or "Creating") in heading.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
syntax: glob
22

3+
.aspire/
4+
**/.aspire/
5+
36
# Python virtual environments
47
.venv
58

docs/app-host/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ For more information, see [.NET Aspire and launch profiles](../fundamentals/laun
5555
|--|--|--|
5656
| `ASPIRE_ALLOW_UNSECURED_TRANSPORT` | `false` | Allows communication with the app host without https. `ASPNETCORE_URLS` (dashboard address) and `ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL` (app host resource service address) must be secured with HTTPS unless true. |
5757
| `ASPIRE_CONTAINER_RUNTIME` | `docker` | Allows the user of alternative container runtimes for resources backed by containers. Possible values are `docker` (default) or `podman`. See [Setup and tooling overview for more details](../fundamentals/setup-tooling.md). |
58+
| `ASPIRE_VERSION_CHECK_DISABLED` | `false` | When set to `true`, .NET Aspire doesn't check for newer versions on startup. |
5859

5960
## Resource service
6061

docs/app-host/eventing.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Eventing in .NET Aspire
33
description: Learn how to use the .NET eventing features with .NET Aspire.
4-
ms.date: 04/04/2025
4+
ms.date: 07/10/2025
55
---
66

77
# Eventing in .NET Aspire
@@ -17,48 +17,55 @@ In this article, you learn how to use the eventing features in .NET Aspire.
1717
The following events are available in the app host and occur in the following order:
1818

1919
1. <xref:Aspire.Hosting.ApplicationModel.BeforeStartEvent>: This event is raised before the app host starts.
20-
1. <xref:Aspire.Hosting.ApplicationModel.AfterEndpointsAllocatedEvent>: This event is raised after the app host allocated endpoints.
20+
1. <xref:Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent>: This event is raised per resource after its endpoints are allocated.
2121
1. <xref:Aspire.Hosting.ApplicationModel.AfterResourcesCreatedEvent>: This event is raised after the app host created resources.
2222

2323
All of the preceding events are analogous to the [app host life cycles](xref:dotnet/aspire/app-host#app-host-life-cycles). That is, an implementation of the <xref:Aspire.Hosting.Lifecycle.IDistributedApplicationLifecycleHook> could handle these events just the same. With the eventing API, however, you can run arbitrary code when these events are raised and event define custom events—any event that implements the <xref:Aspire.Hosting.Eventing.IDistributedApplicationEvent> interface.
2424

2525
### Subscribe to app host events
2626

27-
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:
27+
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 _AppHost.cs_ file:
2828

29-
:::code source="snippets/AspireApp/AspireApp.AppHost/Program.cs":::
29+
:::code source="snippets/AspireApp/AspireApp.AppHost/AppHost.cs":::
3030

3131
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.
3232

3333
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:
3434

35-
:::code language="Plaintext" source="snippets/AspireApp/AspireApp.AppHost/Console.txt" highlight="2,10-14,20":::
35+
:::code language="Plaintext" source="snippets/AspireApp/AspireApp.AppHost/Console.txt" highlight="2,10,12,14,16,22":::
3636

37-
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`.
37+
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 each resource's `ResourceEndpointsAllocatedEvent`, and finally `AfterResourcesCreatedEvent`.
3838

3939
## Resource eventing
4040

4141
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:
4242

43-
1. `InitializeResourceEvent`: Raised by orchestrators to signal to resources that they should initialize themselves.
43+
1. <xref:Aspire.Hosting.ApplicationModel.InitializeResourceEvent>: Raised by orchestrators to signal to resources that they should initialize themselves.
44+
1. <xref:Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent>: Raised when the orchestrator allocates endpoints for a resource.
4445
1. <xref:Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent>: Raised when a connection string becomes available for a resource.
4546
1. <xref:Aspire.Hosting.ApplicationModel.BeforeResourceStartedEvent>: Raised before the orchestrator starts a new resource.
4647
1. <xref:Aspire.Hosting.ApplicationModel.ResourceReadyEvent>: Raised when a resource initially transitions to a ready state.
4748

4849
### Subscribe to resource events
4950

50-
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:
51+
To subscribe to resource events, use the convenience-based extension methods—`On*`. After you have a distributed application builder instance, and a resource builder, walk up to the instance and chain a call to the desired `On*` event API. Consider the following sample _AppHost.cs_ file:
5152

52-
:::code source="snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs":::
53+
:::code source="snippets/AspireApp/AspireApp.ResourceAppHost/AppHost.cs":::
5354

54-
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.
55+
The preceding code subscribes to the `InitializeResourceEvent`, `ResourceReadyEvent`, `ResourceEndpointsAllocatedEvent`, `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>. Chain calls to the `On*` methods to subscribe to the events. The `On*` methods return the same <xref:Aspire.Hosting.ApplicationModel.IResourceBuilder`1> instance, so you can chain multiple calls:
56+
57+
- `OnInitializeResource`: Subscribes to the <xref:Aspire.Hosting.ApplicationModel.InitializeResourceEvent>.
58+
- `OnResourceEndpointsAllocated`: Subscribes to the <xref:Aspire.Hosting.ApplicationModel.ResourceEndpointsAllocatedEvent> event.
59+
- `OnConnectionStringAvailable`: Subscribes to the <xref:Aspire.Hosting.ApplicationModel.ConnectionStringAvailableEvent> event.
60+
- `OnBeforeResourceStarted`: Subscribes to the <xref:Aspire.Hosting.ApplicationModel.BeforeResourceStartedEvent> event.
61+
- `OnResourceReady`: Subscribes to the <xref:Aspire.Hosting.ApplicationModel.ResourceReadyEvent> event.
5562

5663
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:
5764

58-
:::code language="Plaintext" source="snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt" highlight="8,10,12,18":::
65+
:::code language="Plaintext" source="snippets/AspireApp/AspireApp.ResourceAppHost/Console.txt" highlight="8,10,12,14,20":::
5966

6067
> [!NOTE]
61-
> 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).
68+
> Some events block execution. For example, when the `BeforeResourceStartedEvent` is published, the resource startup blocks until all subscriptions for that event on a given resource finish executing. Whether an event blocks or not depends on how you publish it (see the following section).
6269
6370
## Publish events
6471

@@ -74,15 +81,15 @@ Then, you can subscribe and publish the event by calling the either of the follo
7481
When events are dispatched, you can control how the events are dispatched to subscribers. The event dispatch behavior is specified with the `EventDispatchBehavior` enum. The following behaviors are available:
7582

7683
- <xref:Aspire.Hosting.Eventing.EventDispatchBehavior.BlockingSequential?displayProperty=nameWithType>: Fires events sequentially and blocks until they're all processed.
77-
- <xref:Aspire.Hosting.Eventing.EventDispatchBehavior.BlockingConcurrent?displayProperty=nameWithType>: Fires events concurrently and blocks until they are all processed.
84+
- <xref:Aspire.Hosting.Eventing.EventDispatchBehavior.BlockingConcurrent?displayProperty=nameWithType>: Fires events concurrently and blocks until they're all processed.
7885
- <xref:Aspire.Hosting.Eventing.EventDispatchBehavior.NonBlockingSequential?displayProperty=nameWithType>: Fires events sequentially but doesn't block.
7986
- <xref:Aspire.Hosting.Eventing.EventDispatchBehavior.NonBlockingConcurrent?displayProperty=nameWithType>: Fires events concurrently but doesn't block.
8087

8188
The default behavior is `EventDispatchBehavior.BlockingSequential`. To override this behavior, when calling a publishing API such as <xref:Aspire.Hosting.Eventing.IDistributedApplicationEventing.PublishAsync*>, provide the desired behavior as an argument.
8289

8390
## App Host life cycle events
8491

85-
As you have seen, eventing is the most flexible approach. However, in this section you learn about the alternative: life cycle events.
92+
Eventing offers the most flexibility. However, this section explains the alternative: life cycle events.
8693

8794
The .NET Aspire app host exposes several life cycles that you can hook into by implementing the <xref:Aspire.Hosting.Lifecycle.IDistributedApplicationLifecycleHook> interface. The following lifecycle methods are available:
8895

@@ -96,7 +103,7 @@ The .NET Aspire app host exposes several life cycles that you can hook into by i
96103

97104
To register a life cycle hook, implement the <xref:Aspire.Hosting.Lifecycle.IDistributedApplicationLifecycleHook> interface and register the hook with the app host using the <xref:Aspire.Hosting.Lifecycle.LifecycleHookServiceCollectionExtensions.AddLifecycleHook*> API:
98105

99-
:::code source="../fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/Program.cs":::
106+
:::code source="../fundamentals/snippets/lifecycles/AspireApp/AspireApp.AppHost/AppHost.cs":::
100107

101108
The preceding code:
102109

docs/app-host/snippets/AspireApp/AspireApp.AppHost/Program.cs renamed to docs/app-host/snippets/AspireApp/AspireApp.AppHost/AppHost.cs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,21 @@
1515
.WaitFor(apiService);
1616

1717
builder.Eventing.Subscribe<ResourceEndpointsAllocatedEvent>(
18-
(@event, cancellationToken) =>
19-
{
20-
// The event doesn't expose an IServiceProvider, just write to the console.
21-
Console.WriteLine($"""
22-
3. '{@event.Resource.Name}' ResourceEndpointsAllocatedEvent
23-
""");
24-
25-
return Task.CompletedTask;
26-
});
27-
28-
builder.Eventing.Subscribe<BeforeStartEvent>(
2918
static (@event, cancellationToken) =>
3019
{
3120
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
3221

33-
logger.LogInformation("1. BeforeStartEvent");
22+
logger.LogInformation("2. \"{ResourceName}\" ResourceEndpointsAllocatedEvent", @event.Resource.Name);
3423

3524
return Task.CompletedTask;
3625
});
3726

38-
builder.Eventing.Subscribe<AfterEndpointsAllocatedEvent>(
27+
builder.Eventing.Subscribe<BeforeStartEvent>(
3928
static (@event, cancellationToken) =>
4029
{
4130
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
4231

43-
logger.LogInformation("2. AfterEndpointsAllocatedEvent");
32+
logger.LogInformation("1. BeforeStartEvent");
4433

4534
return Task.CompletedTask;
4635
});
@@ -50,7 +39,7 @@
5039
{
5140
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
5241

53-
logger.LogInformation("4. AfterResourcesCreatedEvent");
42+
logger.LogInformation("3. AfterResourcesCreatedEvent");
5443

5544
return Task.CompletedTask;
5645
});

docs/app-host/snippets/AspireApp/AspireApp.AppHost/AspireApp.AppHost.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" />
3+
<Sdk Name="Aspire.AppHost.Sdk" Version="9.4.0" />
44

55
<PropertyGroup>
66
<OutputType>Exe</OutputType>
@@ -16,8 +16,8 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.1" />
20-
<PackageReference Include="Aspire.Hosting.Redis" Version="9.3.1" />
19+
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.4.0" />
20+
<PackageReference Include="Aspire.Hosting.Redis" Version="9.4.0" />
2121
</ItemGroup>
2222

2323
</Project>
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
info: Program[0]
22
1. BeforeStartEvent
33
info: Aspire.Hosting.DistributedApplication[0]
4-
Aspire version: 9.3.0-preview.1.25262.2+6d54dc081cd2e7ea435e33f7c0e62ff6946ae66d
4+
Aspire version: 9.4.0
55
info: Aspire.Hosting.DistributedApplication[0]
66
Distributed application starting.
77
info: Aspire.Hosting.DistributedApplication[0]
88
Application host directory is: ../AspireApp/AspireApp.AppHost
99
info: Program[0]
10-
2. AfterEndpointsAllocatedEvent
11-
3. 'aspire-dashboard' ResourceEndpointsAllocatedEvent
12-
3. 'cache' ResourceEndpointsAllocatedEvent
13-
3. 'apiservice' ResourceEndpointsAllocatedEvent
14-
3. 'webfrontend' ResourceEndpointsAllocatedEvent
10+
2. "cache" ResourceEndpointsAllocatedEvent
11+
info: Program[0]
12+
2. "apiservice" ResourceEndpointsAllocatedEvent
13+
info: Program[0]
14+
2. "webfrontend" ResourceEndpointsAllocatedEvent
15+
info: Program[0]
16+
2. "aspire-dashboard" ResourceEndpointsAllocatedEvent
1517
info: Aspire.Hosting.DistributedApplication[0]
1618
Now listening on: https://localhost:17178
1719
info: Aspire.Hosting.DistributedApplication[0]
1820
Login to the dashboard at https://localhost:17178/login?t=<YOUR_TOKEN>
1921
info: Program[0]
20-
4. AfterResourcesCreatedEvent
22+
3. AfterResourcesCreatedEvent
2123
info: Aspire.Hosting.DistributedApplication[0]
2224
Distributed application started. Press Ctrl+C to shut down.

docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/Program.cs renamed to docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AppHost.cs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,52 @@
55

66
var cache = builder.AddRedis("cache");
77

8-
builder.Eventing.Subscribe<ResourceReadyEvent>(
9-
cache.Resource,
10-
static (@event, cancellationToken) =>
8+
cache.OnResourceReady(static (resource, @event, cancellationToken) =>
119
{
1210
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
1311

14-
logger.LogInformation("4. ResourceReadyEvent");
12+
logger.LogInformation("5. OnResourceReady");
1513

1614
return Task.CompletedTask;
1715
});
1816

19-
builder.Eventing.Subscribe<InitializeResourceEvent>(cache.Resource,
20-
static (@event, cancellationToken) =>
17+
cache.OnInitializeResource(
18+
static (resource, @event, cancellationToken) =>
2119
{
2220
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
2321

24-
logger.LogInformation("1. InitializeResourceEvent");
22+
logger.LogInformation("1. OnInitializeResource");
2523

2624
return Task.CompletedTask;
2725
});
2826

29-
builder.Eventing.Subscribe<BeforeResourceStartedEvent>(
30-
cache.Resource,
31-
static (@event, cancellationToken) =>
27+
cache.OnBeforeResourceStarted(
28+
static (resource, @event, cancellationToken) =>
3229
{
3330
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
3431

35-
logger.LogInformation("3. BeforeResourceStartedEvent");
32+
logger.LogInformation("4. OnBeforeResourceStarted");
33+
34+
35+
return Task.CompletedTask;
36+
});
37+
38+
cache.OnResourceEndpointsAllocated(
39+
static (resource, @event, cancellationToken) =>
40+
{
41+
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
42+
43+
logger.LogInformation("2. OnResourceEndpointsAllocated");
3644

3745
return Task.CompletedTask;
3846
});
3947

40-
builder.Eventing.Subscribe<ConnectionStringAvailableEvent>(
41-
cache.Resource,
42-
static (@event, cancellationToken) =>
48+
cache.OnConnectionStringAvailable(
49+
static (resource, @event, cancellationToken) =>
4350
{
4451
var logger = @event.Services.GetRequiredService<ILogger<Program>>();
4552

46-
logger.LogInformation("2. ConnectionStringAvailableEvent");
53+
logger.LogInformation("3. OnConnectionStringAvailable");
4754

4855
return Task.CompletedTask;
4956
});

docs/app-host/snippets/AspireApp/AspireApp.ResourceAppHost/AspireApp.ResourceAppHost.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<Sdk Name="Aspire.AppHost.Sdk" Version="9.2.0" />
3+
<Sdk Name="Aspire.AppHost.Sdk" Version="9.4.0" />
44

55
<PropertyGroup>
66
<OutputType>Exe</OutputType>
@@ -16,8 +16,8 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.3.1" />
20-
<PackageReference Include="Aspire.Hosting.Redis" Version="9.3.1" />
19+
<PackageReference Include="Aspire.Hosting.AppHost" Version="9.4.0" />
20+
<PackageReference Include="Aspire.Hosting.Redis" Version="9.4.0" />
2121
</ItemGroup>
2222

2323
</Project>

0 commit comments

Comments
 (0)