Skip to content
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 src/frontend/config/sidebar/reference.topics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,10 @@ export const referenceTopics: StarlightSidebarTopicsUserConfig[number] = {
label: 'ASPIREHOSTINGPYTHON001',
link: '/diagnostics/aspirehostingpython001',
},
{
label: 'ASPIREPERSISTENCE001',
link: '/diagnostics/aspirepersistence001',
},
{
label: 'ASPIREPIPELINES001',
link: '/diagnostics/aspirepipelines001',
Expand Down
60 changes: 57 additions & 3 deletions src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Configure resource lifetimes in Aspire
description: Learn how session, persistent, resource-scoped, and parent-process lifetimes control Aspire containers, executables, and projects.
description: Learn how session, persistent, resource-scoped, and parent-process lifetimes, plus explicit start, control Aspire containers, executables, and projects.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';
Expand All @@ -14,7 +14,7 @@ Aspire resources support a number of different lifetime modes. For example, the
Resource lifetimes apply to containers, executables, and projects. Persistent executable and project lifetimes are experimental in Aspire 13.4. You can use different lifetimes for resources that take time to initialize, need stable local endpoints, should remain available while you restart or rebuild the AppHost, or need to match another resource's lifetime.

:::caution[Experimental shared lifetime APIs]
The shared lifetime APIs are experimental and emit diagnostic `ASPIREPERSISTENCE001`. The existing container-specific `WithLifetime(ContainerLifetime.Persistent)` and `WithLifetime(ContainerLifetime.Session)` APIs remain supported for container resources.
The shared lifetime APIs are experimental and emit the [`ASPIREPERSISTENCE001`](/diagnostics/aspirepersistence001/) diagnostic. The existing container-specific `WithLifetime(ContainerLifetime.Persistent)` and `WithLifetime(ContainerLifetime.Session)` APIs remain supported for container resources.
:::

## Lifetime modes
Expand Down Expand Up @@ -44,7 +44,8 @@ Persistent containers use proxied endpoints by default, just like session contai
You can still configure endpoint proxy behavior explicitly on any persistent resource. Set `isProxied: false` on an individual endpoint, or call `WithEndpointProxySupport(false)` to make every endpoint on a resource proxyless. When a proxyless endpoint doesn't specify a public `port`, Aspire allocates one before the resource is created. For persistent resources, Aspire stores the allocated port in user secrets when user secrets are available and reuses it on later AppHost runs.

<LearnMore>
For more information, see [Allocate ports for dynamic proxyless endpoints](/fundamentals/networking-overview/#allocate-ports-for-dynamic-proxyless-endpoints).
For more information, see [Allocate ports for dynamic proxyless
endpoints](/fundamentals/networking-overview/#allocate-ports-for-dynamic-proxyless-endpoints).
</LearnMore>

:::danger[Persistent container &ne; persistent data]
Expand Down Expand Up @@ -75,6 +76,59 @@ Use resource-scoped lifetime when a companion resource should follow the lifetim

Aspire evaluates the source resource's lifetime when it prepares the application model, so later lifetime changes to the source resource are reflected by the dependent resource. The source and dependent resources must both support lifetime configuration.

## Defer resource start with explicit start

Use `WithExplicitStart()` to prevent a resource from starting automatically with the rest of the AppHost. The resource appears in the dashboard but remains stopped until you start it manually.

Starting in Aspire 13.5, `WithExplicitStart()` also affects when execution configuration callbacks (for example, `WithEnvironment` and `WithArgs`) are evaluated, depending on whether the resource is session-scoped or persistent.

### Session-scoped explicit-start resources

For session-scoped resources (the default lifetime), Aspire defers Developer Control Plane (DCP) registration until you manually start the resource from the dashboard. This means execution configuration callbacks — such as `WithEnvironment(context => ...)` — are **not** evaluated during AppHost startup. They run only when the resource is manually started.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

// The callback below is NOT evaluated during AppHost startup.
// It runs only when the resource is manually started from the dashboard.
var job = builder.AddExecutable("batch-job", "dotnet", ".", "run", "--project", "BatchJob")
.WithExplicitStart()
.WithEnvironment(async context =>
{
// Prompt or compute dynamic configuration at start time
context.EnvironmentVariables["API_KEY"] = await GetApiKeyAsync();
});

builder.Build().Run();
```

:::note[Placeholder API used for demonstration]
`GetApiKeyAsync` is a placeholder used to demonstrate deferred, callback-based configuration. Its implementation isn't shown because the relevant behavior is retrieving a value when the callback runs and assigning that value to an environment variable on the target resource.
:::

### Persistent explicit-start resources

For persistent resources, Aspire must register the resource with the Developer Control Plane (DCP) immediately at startup so it can discover any existing running instance. However, when you manually start a persistent explicit-start resource, Aspire patches the existing DCP resource to start it rather than deleting and recreating it. This means the execution configuration callbacks run once during startup registration and are **not** re-evaluated when you manually start the resource.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

// Persistent explicit-start resources are registered at startup to detect existing instances.
// The callback runs during startup registration — not again when manually started.
#pragma warning disable ASPIREPERSISTENCE001
var cache = builder.AddContainer("long-lived-cache", "my-cache-image")
.WithPersistentLifetime()
.WithExplicitStart()
.WithEnvironment(context =>
{
context.EnvironmentVariables["CACHE_SIZE"] = "512mb";
});

#pragma warning restore ASPIREPERSISTENCE001

builder.Build().Run();
```

## Configure a persistent container

For new code, configure a persistent container with `WithPersistentLifetime()`:
Expand Down
80 changes: 80 additions & 0 deletions src/frontend/src/content/docs/diagnostics/aspirepersistence001.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: Compiler Error ASPIREPERSISTENCE001
seoTitle: 'ASPIREPERSISTENCE001: Experimental resource lifetime APIs'
description: Learn what causes the Aspire compiler error ASPIREPERSISTENCE001 and how to suppress it when using experimental resource lifetime APIs.
---

import { Badge } from '@astrojs/starlight/components';

<Badge
text="Version introduced: 13.4"
variant="note"
size="large"
class:list={'mb-1'}
/>

> Resource lifetime types and members are for evaluation purposes only and are subject to change or removal in future updates. Suppress this diagnostic to proceed.

This diagnostic error is reported when a C# AppHost uses the experimental shared resource lifetime APIs. These APIs configure session, persistent, parent-process, and resource-scoped lifetimes across supported resources.

The following APIs and types are protected by this diagnostic:

- `WithSessionLifetime`
- `WithPersistentLifetime`
- `WithParentProcessLifetime`
- `WithLifetimeOf`
- `PersistenceAnnotation`
- `PersistenceMode`

## Example

The following code generates `ASPIREPERSISTENCE001`:

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var worker = builder.AddExecutable("worker", "node", "../worker", "server.js")
.WithPersistentLifetime();

builder.Build().Run();
```

## To correct this error

Suppress the diagnostic when you intentionally opt in to the experimental shared lifetime APIs. For container resources, you can instead use the supported container-specific `WithLifetime(ContainerLifetime.Persistent)` and `WithLifetime(ContainerLifetime.Session)` APIs.

Suppress the error with one of the following methods:

- Set the severity of the rule in the _.editorconfig_ file.

```ini title=".editorconfig"
[*.{cs,vb}]
dotnet_diagnostic.ASPIREPERSISTENCE001.severity = none
```

For more information about editor config files, see [Configuration files for code analysis rules](/diagnostics/overview/#suppress-in-the-editorconfig-file).

- Add the following `PropertyGroup` to your project file:

```xml title="C# project file"
<PropertyGroup>
<NoWarn>$(NoWarn);ASPIREPERSISTENCE001</NoWarn>
</PropertyGroup>
```

- Suppress the diagnostic in code with the `#pragma warning disable ASPIREPERSISTENCE001` directive:

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

#pragma warning disable ASPIREPERSISTENCE001
var worker = builder.AddExecutable("worker", "node", "../worker", "server.js")
.WithPersistentLifetime();
#pragma warning restore ASPIREPERSISTENCE001

builder.Build().Run();
```

## See also

- [Configure resource lifetimes](/app-host/resource-lifetimes/)
1 change: 1 addition & 0 deletions src/frontend/src/content/docs/diagnostics/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The following table lists the possible MSBuild and analyzer warnings and errors
| [ASPIREJAVASCRIPT001](/diagnostics/aspirejavascript001/) | (Experimental) Warning | JavaScript hosting and publishing APIs are for evaluation purposes only and are subject to change or removal in future updates. |
| [ASPIREHOSTINGPYTHON001](/diagnostics/aspirehostingpython001/) | (Experimental) Error | `AddPythonApp` is for evaluation purposes only and is subject to change or removal in future updates. |
| [ASPIREMCP001](/diagnostics/aspiremcp001/) | (Experimental) Warning | <abbr title="Model Context Protocol" data-tooltip-placement="top">MCP</abbr> server types and members are for evaluation purposes only and are subject to change or removal in future updates. |
| [ASPIREPERSISTENCE001](/diagnostics/aspirepersistence001/) | (Experimental) Error | Resource lifetime types and members are for evaluation purposes only and are subject to change or removal in future updates. |
| [ASPIREPIPELINES001](/diagnostics/aspirepipelines001/) | (Experimental) Error | Pipeline infrastructure APIs are for evaluation purposes only and are subject to change or removal in future updates. |
| [ASPIREPIPELINES002](/diagnostics/aspirepipelines002/) | (Experimental) Error | Deployment state manager APIs are for evaluation purposes only and are subject to change or removal in future updates. |
| [ASPIREPIPELINES003](/diagnostics/aspirepipelines003/) | (Experimental) Error | Container image build APIs are for evaluation purposes only and are subject to change or removal in future updates. |
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/src/content/docs/whats-new/aspire-13-4.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ Persistent lifetimes now extend beyond containers to executables and projects. C

<Aside type="caution">
The shared persistent and session lifetime APIs are experimental and emit
the `ASPIREPERSISTENCE001` diagnostic, which C# callers must suppress to use
the [`ASPIREPERSISTENCE001`](/diagnostics/aspirepersistence001/) diagnostic,
which C# callers must suppress to use
them. Persistent executables and projects default to proxyless endpoints,
must use concrete ports for executables, don't support replicas, and aren't
compatible with Aspire IDE debugging sessions. Persistent containers use
Expand Down
Loading