The repository ships two example AppHosts that compose the same set of resources in two different styles:
src/src/Example.AppHost— the source-generated pattern (attributes, no hand-written wiring).src/src/Example.ManualAppHost— the manual pattern (hand-writtenHostKitBaseandResourceKitBasecomposition, no generator).
Both hosts build the same logical resources through Purview.Aspire.ResourceKit.Example constants in
Example.ServiceDefaults/Platform.cs:
| Resource | Name | Aspire type |
|---|---|---|
| Postgres | postgres (+ db database) |
AzurePostgresFlexibleServerResource |
| Azure Storage | azure-storage (+ blob) |
AzureStorageResource |
| Redis | redis |
AzureManagedRedisResource |
| Key Vault | kv |
AzureKeyVaultResource |
| API | api |
ProjectResource (Projects.Example_Service) |
| Publish marker | publish-marker |
ParameterResource |
Key Vault and the publish marker are publish-only (IsResourceEnabled returns
builder.ExecutionContext.IsPublishMode).
AppHost.cs registers everything with a single generated call:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAspireResourceKit();
var app = builder.Build();
await app.RunAsync();The host kit is a one-line attribute declaration:
[HostKit]
sealed partial class ExampleHostKit;Each resource is a [ResourceDefinition<TResource>] partial class that supplies the overrides. For
example, the API kit also wires dependencies in ConfigureResource() and can now forward a populated
options object to a destination resource:
[ResourceDefinition<Projects.Example_Service>(Platform.ResourceKits.API)]
sealed partial class ExampleAPIKit
{
protected override IResourceBuilder<ProjectResource> BuildResource(IDistributedApplicationBuilder builder) =>
builder.AddProject<Projects.Example_Service>(Name).WithUrl("/health", "Health");
protected override void ConfigureResource()
{
if (HostKit.PublishMarker.IsEnabled)
ResourceBuilder.WithEnvironment(Options.PublishEnvironmentVariableName, HostKit.PublishMarker);
ResourceBuilder.WithEnvironment(
OptionsHelper.Environment(
new DemoServiceEnvironmentOptions
{
Service = new()
{
Name = Name,
Enabled = true,
Labels =
{
["region"] = "west",
},
},
Replicas = [1, 2, 3],
Routes =
{
["health"] = "/health",
},
}
)
.Override(static o => o.Service.Name = "api")
.Ignore(static o => o.Service.Labels)
.Build()
);
ResourceBuilder.WithReference(HostKit.Postgres.Database).WaitFor(HostKit.Postgres.Database);
ResourceBuilder.WithReference(HostKit.AzureStorage.Blobs).WaitFor(HostKit.AzureStorage.Blobs);
if (HostKit.KeyVault.IsEnabled)
ResourceBuilder.WithReference(HostKit.KeyVault).WaitFor(HostKit.KeyVault);
if (HostKit.Redis.IsEnabled)
ResourceBuilder.WithReference(HostKit.Redis).WaitFor(HostKit.Redis);
}
partial class ExampleAPIKitOptions
{
[Required(AllowEmptyStrings = false)]
public string PublishEnvironmentVariableName { get; set; } = "PUBLISH_MARKER";
}
sealed class DemoServiceEnvironmentOptions
{
public DemoServiceOptions Service { get; set; } = new();
public List<int> Replicas { get; set; } = [];
public Dictionary<string, string> Routes { get; set; } = [];
}
sealed class DemoServiceOptions
{
public string Name { get; set; } = string.Empty;
public bool Enabled { get; set; }
public Dictionary<string, string> Labels { get; set; } = [];
}
}The generator emits the host kit members, options, ResourceKitBase<TResource> base classes, and the
extension method. See Generated Output.
AppHost.cs registers a host kit instance directly via the generic extension:
builder.AddAspireResourceKit<ExampleHostKit>();The host kit is hand-written and composes the resource kits:
sealed class ExampleHostKit : HostKitBase<ExampleHostKit>
{
public AzureStorageKit AzureStorage { get; init; }
public ExampleAPIKit ExampleAPI { get; init; }
// ...
public ExampleHostKit()
{
AzureStorage = new(this);
ExampleAPI = new(this);
// ...
AddResource(AzureStorage);
AddResource(ExampleAPI);
// ...
}
}Each resource kit is a hand-written partial class deriving from the runtime base, with a primary constructor that takes the host kit:
sealed partial class ExampleAPIKit(ExampleHostKit hostKit)
: ResourceKitBase<ExampleHostKit, ProjectResource>(hostKit, Platform.ResourceKits.API)
{
protected override IResourceBuilder<ProjectResource> BuildResource(IDistributedApplicationBuilder builder) =>
builder.AddProject<Projects.Example_Service>(Name);
protected override void ConfigureResource()
{
if (HostKit.PublishMarker.IsEnabled)
ResourceBuilder.WithEnvironment("PUBLISH_MARKER", HostKit.PublishMarker);
ResourceBuilder.WithReference(HostKit.Postgres.Database).WaitFor(HostKit.Postgres.Database);
// ...
}
}| Consideration | Generated | Manual |
|---|---|---|
| Boilerplate | Minimal (attributes + overrides) | Explicit (base classes, constructors, AddResource) |
| Options | Generated, typed, bindable | You manage options yourself |
| Diagnostics | Analyzer checks model rules | No generator diagnostics |
| Testability | Both test well | Lifecycle is directly unit-testable |
| Best for | Most AppHosts | Learning the model, or when you want full control |
Both patterns produce the same runtime behavior: instantiate resources, Build enabled resources,
Configure enabled resources.