Learn how to wire dependencies between resources using WithReference
Distributed applications are composed of interconnected services that need to communicate with each other. .NET Aspire makes it simple to wire these dependencies using the WithReference method and the resource graph.
When you call WithReference, Aspire creates a dependency relationship between resources, forming a directed acyclic graph (DAG). This graph determines:
Startup order - Dependencies start before dependents
Service discovery - Connection information flows from dependencies to dependents
Environment injection - Endpoints and connection strings are automatically injected
var cache = builder.AddRedis("cache");var db = builder.AddPostgres("postgres").AddDatabase("catalogdb");var api = builder.AddProject<Projects.Api>("api") .WithReference(cache) // api depends on cache .WithReference(db); // api depends on db
In this example:
cache and postgres start first (no dependencies)
catalogdb starts after postgres is ready
api starts after both cache and catalogdb are ready
var db = builder.AddPostgres("postgres") .AddDatabase("catalogdb");var api = builder.AddProject<Projects.Api>("api") .WithReference(db, connectionName: "CatalogDatabase");
By default, WithReference injects connection strings, endpoints, and service discovery information. You can customize this with WithReferenceEnvironment:
var cache = builder.AddRedis("cache");var api = builder.AddProject<Projects.Api>("api") .WithReferenceEnvironment(ReferenceEnvironmentInjectionFlags.ServiceDiscovery) .WithReference(cache);
Available flags:
ConnectionString - Inject connection strings
Endpoints - Inject direct endpoint variables (e.g., CACHE_TCP)
ServiceDiscovery - Inject service discovery format (e.g., services__cache__tcp__0)
Control what happens when a dependency fails to start:
Default Behavior
Continue on Failure
Stop on Failure
By default, resources wait for dependencies to become healthy:
var db = builder.AddPostgres("postgres");var api = builder.AddProject<Projects.Api>("api") .WithReference(db);
The api waits for postgres to be healthy before starting.
Start the resource even if dependency fails:
var cache = builder.AddRedis("cache") .WithWaitBehavior(WaitBehavior.WaitOnResourceUnavailable);var api = builder.AddProject<Projects.Api>("api") .WithReference(cache);
The api will start even if cache is unavailable.
Stop the entire application if dependency fails:
var db = builder.AddPostgres("postgres") .WithWaitBehavior(WaitBehavior.StopOnResourceUnavailable);var api = builder.AddProject<Projects.Api>("api") .WithReference(db);
If postgres fails to start, the entire application stops.
// Database depends on migration toolvar migrator = builder.AddProject<Projects.Migrator>("migrator");var db = builder.AddPostgres("postgres") .AddDatabase("catalogdb") .WaitFor(migrator); // Wait for migrator to complete// API depends on databasevar api = builder.AddProject<Projects.Api>("api") .WithReference(db);
var externalApi = builder.AddExternalService("payment-api", new Uri("https://api.payment.com/"));var api = builder.AddProject<Projects.Api>("api") .WithReference(externalApi);
Or use a parameter for dynamic configuration:
var apiUrl = builder.AddParameter("payment-api-url");var externalApi = builder.AddExternalService("payment-api", apiUrl);var api = builder.AddProject<Projects.Api>("api") .WithReference(externalApi);
var db = builder.AddPostgres("postgres") .AddDatabase("appdb");var migrator = builder.AddProject<Projects.Migrator>("migrator") .WithReference(db);db.WaitFor(migrator);var api = builder.AddProject<Projects.Api>("api") .WithReference(db);