Skip to content

Resource API Patterns

Aspire’s resource model allows you to define and configure resources in a structured way, enabling seamless integration and management of your application’s components. This guide provides details the common patterns for adding and configuring resources in Aspire.

Aspire separates resource data models from behavior using fluent extension methods.

  • Resource classes define only constructors and properties.
  • Extension methods implement resource creation, configuration, and runtime wiring.

This guide describes each pattern and shows a verbatim Redis example at the end. It also covers how to publish manifests via custom resources.

An AddX(...) method executes:

  1. Validate inputs (builder, name, required arguments).
  2. Instantiate the data-only resource (new TResource(...)).
  3. Register it with builder.AddResource(resource).
  4. Optional wiring of endpoints, health checks, container settings, environment variables, command-line arguments, and event subscriptions.
public static IResourceBuilder<TResource> AddX(
this IDistributedApplicationBuilder builder,
[ResourceName] string name,
/* optional parameters */)
{
// 1. Validate inputs
// 2. Instantiate resource
// 3. builder.AddResource(resource)
// 4. Optional wiring:
// .WithEndpoint(...)
// .WithHealthCheck(...)
// .WithImage(...)
// .WithEnvironment(...)
// .WithArgs(...)
// Eventing.Subscribe<...>(...)
}

Endpoints:

.WithEndpoint(port: hostPort, targetPort: containerPort, name: endpointName)

Health checks:

.WithHealthCheck(healthCheckKey)

Container images / registries:

.WithImage(imageName, imageTag)
.WithImageRegistry(registryUrl)

Entrypoint and args:

.WithEntrypoint("/bin/sh")
.WithArgs(context => { /* build args */ return Task.CompletedTask; })

Environment variables:

.WithEnvironment(context => new("ENV_VAR", valueProvider))

Event subscriptions:

builder.Eventing.Subscribe<EventType>(resource, handler);
StepCall/MethodPurpose
ValidateArgumentNullException.ThrowIfNull(...)Ensure non-null builder, name, and args
Instantiatenew TResource(name, …)Create data-only instance
Registerbuilder.AddResource(resource)Add resource to the application model
Optional wiring.WithEndpoint(…), .WithHealthCheck(…), .WithImage(…), .WithEnvironment(…), .WithArgs(…), Eventing.Subscribe(…)Configure container details, wiring, and runtime hooks

WithX(...) methods attach annotations to resource builders.

public static IResourceBuilder<TResource> WithX(
this IResourceBuilder<TResource> builder,
FooOptions options) =>
builder.WithAnnotation(new FooAnnotation(options));
  • Target: IResourceBuilder<TResource>.
  • Action: WithAnnotation(...).
  • Returns: IResourceBuilder<TResource>.
MethodTargetAction
WithX(...)IResourceBuilder<TResource>Attaches XAnnotation using the WithAnnotation API.
ReturnsIResourceBuilder<TResource>Enables fluent chaining .

Annotations are public metadata types implementing IResourceAnnotation. They can be added or removed dynamically at runtime via hooks or events. Consumers can query annotations using TryGetLastAnnotation<T>() when necessary.

public sealed record PersistenceAnnotation(
TimeSpan? Interval,
int KeysChangedThreshold) : IResourceAnnotation;
builder.WithAnnotation(new PersistenceAnnotation(
TimeSpan.FromSeconds(60),
100));
ConceptPatternNotes
Annotation Typepublic record XAnnotation(...) : IResourceAnnotationPublic to support dynamic runtime use.
Attachbuilder.WithAnnotation(new XAnnotation(...))Adds metadata to resource builder.
Queryresource.TryGetLastAnnotation<XAnnotation>(out var a)Consumers inspect annotations as needed.

Custom value objects defer evaluation and allow the framework to discover dependencies between resources.

InterfaceMemberModePurpose
IValueProviderValueTask<string?> GetValueAsync(CancellationToken)RunResolve live values at runtime
IManifestExpressionProviderstring ValueExpression { get; }PublishEmit structured expressions in manifests
IValueWithReferences (opt.)IEnumerable<object> References { get; }Both (if needed)Declare dependencies on other resources
  • Implement IValueProvider and IManifestExpressionProvider on all structured value types.
  • Implement IValueWithReferences only when your type holds resource references.
builder.WithEnvironment(context =>
new("REDIS_CONNECTION_STRING", redis.GetConnectionStringAsync));
Example: BicepOutputReference
public sealed partial class BicepOutputReference :
IManifestExpressionProvider,
IValueProvider,
IValueWithReferences
{
public string ValueExpression { get; }
public ValueTask<string?> GetValueAsync(CancellationToken cancellationToken = default);
IEnumerable<object> IValueWithReferences.References { get; }
}
public static IResourceBuilder<T> WithEnvironment<T>(
this IResourceBuilder<T> builder,
string name,
BicepOutputReference bicepOutputReference)
where T : IResourceWithEnvironment
{
return builder.WithAnnotation(
new EnvironmentVariableAnnotation(name, bicepOutputReference));
}
ConceptPatternPurpose
IValueProviderGetValueAsync(...)Deferred runtime resolution
IManifestExpressionProviderValueExpressionStructured publish-time expression
IValueWithReferences (opt.)ReferencesDeclare resource dependencies
WithEnvironment(...)new("NAME", valueProvider)Attach structured values unflattened
Ask & Answer Collaborate Community Discuss Watch