Watch Aspire live streamsBelgelerAspire'ı deneyin
Watch Aspire live streamsBelgelerDeneyin

Project resources

Bu içerik henüz dilinizde mevcut değil.

.NET logo

This article is the reference for modeling .NET projects as first-class Aspire resources in your AppHost. It enumerates the AppHost APIs — with examples for both AppHost.cs and apphost.mts — that you use to add, configure, and connect a .NET project resource in your AppHost project.

Use project resources when you need to:

  • Orchestrate an ASP.NET Core, worker, or console project from an AppHost.
  • Connect the project to other Aspire resources with withReference, service discovery, and generated connection strings.
  • Reuse launch profile settings and ASP.NET Core endpoint discovery from launchSettings.json.

In a C# AppHost, AddProject<T> references the target project through a generated Projects.* type, created from a ProjectReference in the AppHost .csproj. In a TypeScript AppHost, addProject(name, path) takes the path to the .csproj file directly — no generated type or project file reference is needed.

For single-file apps or quick experiments that don’t need a ProjectReference, use C# file-based apps instead.

In a TypeScript AppHost, pass the path to the .csproj file directly — no ProjectReference in the AppHost project file is needed:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const api = await builder.addProject("api", "../ApiService/ApiService.csproj");
await builder.build().run();

Project resources participate fully in the Aspire application model. You can reference other resources, flow connection strings and service discovery information, and expose endpoints like any other Aspire resource:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const cache = await builder.addRedis("cache");
const postgres = await builder.addPostgres("postgres");
const db = await postgres.addDatabase("appdb");
await builder.addProject("api", "../ApiService/ApiService.csproj")
.withReference(cache)
.withReference(db)
.withExternalHttpEndpoints();
await builder.build().run();

The referenced project receives connection strings and service discovery information through environment variables, just like other Aspire resources.

Project resources read launch profiles from the target project’s launchSettings.json file:

launchSettings.json
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"applicationUrl": "http://localhost:5066",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"applicationUrl": "https://localhost:7239;http://localhost:5066",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Use the launchProfileName option when you want a specific profile:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder.addProject("inventoryservice", "../InventoryService/InventoryService.csproj", {
launchProfileOrOptions: "https",
});
await builder.build().run();

The launchProfileName argument has the highest precedence. When you don’t specify it, Aspire selects the effective launch profile in this order:

  1. The explicit launchProfileName argument, if supplied.
  2. The AppHost default launch profile from AppHost:DefaultLaunchProfileName or DOTNET_LAUNCH_PROFILE.
  3. The first profile in launchSettings.json.
  4. No launch profile.

To force a project resource to run without a launch profile, pass launchProfileName: null:

The TypeScript AppHost doesn’t currently expose a way to explicitly disable launch profile selection. Omit launchProfileOrOptions to fall through to the default profile-selection order described above.

For consistency with dotnet run and dotnet watch, Aspire sets DOTNET_LAUNCH_PROFILE on the launched project when an effective launch profile is selected. The selected profile’s environmentVariables are also passed through to the process.

The applicationUrl values in a project’s launchSettings.json are the project-resource equivalent of configuring explicit HTTP and HTTPS endpoints:

apphost.mts
await builder.addProject("frontend", "../Networking_Frontend/Networking_Frontend.csproj")
.withHttpEndpoint({ port: 5066 })
.withHttpsEndpoint({ port: 7239 });

If there’s no launchSettings.json file or selected launch profile, project resources don’t get HTTP or HTTPS bindings by default.

When you want to ignore launch profile endpoints or prefer Kestrel endpoint configuration, use the configure callback. For ASP.NET Core projects, Aspire can also read endpoint information from Kestrel configuration:

appsettings.Development.json
{
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://*:5271"
}
}
}
}

With a Kestrel endpoint configured, remove any applicationUrl from launchSettings.json before telling the project resource to use Kestrel endpoints.

The TypeScript AppHost doesn’t currently expose a configure callback for addProject. To use Kestrel endpoints, call withHttpsEndpoint() directly and omit launchProfileOrOptions to prevent launch-profile endpoints from conflicting:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder.addProject("inventoryservice", "../InventoryService/InventoryService.csproj")
.withHttpsEndpoint();
await builder.build().run();

For ASP.NET Core projects, Aspire reads the selected launch profile and can create endpoints from the applicationUrl field in launchSettings.json. You can then customize those endpoints with methods like WithEndpoint:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder.addProject("inventoryservice", "../InventoryService/InventoryService.csproj")
.withEndpointCallback("https", async endpoint => {
await endpoint.isProxied.set(false);
});
await builder.build().run();

For more information about launch profile selection and endpoint generation, see Launch profiles.

When you define a host port for a project resource, Aspire assigns that host port to the proxy in front of the project, not directly to the underlying service. This keeps single-instance and replicated projects consistent, and it means WithReference consumers use the proxy endpoint from the generated environment variables.

apphost.mts
await builder.addProject("frontend", "../Networking_Frontend/Networking_Frontend.csproj")
.withHttpEndpoint({ port: 5066 })
.withReplicas(2);

The following diagram shows this setup:

Aspire frontend app networking diagram with specific host port and two replicas.

With that configuration:

  • The proxy listens on host port 5066.
  • Each project replica listens on its own random internal port.
  • The project receives its internal binding through ASPNETCORE_URLS.
  • Browsers and dependent resources talk to the proxy endpoint, not directly to a replica.

Without WithReplicas, the proxy still fronts the project resource. The host port stays stable, while the project itself listens on a random internal port.

apphost.mts
await builder.addProject("frontend", "../Networking_Frontend/Networking_Frontend.csproj")
.withHttpEndpoint({ port: 5066 });
Aspire frontend app networking diagram with specific host port and random port.

In this configuration:

  • The proxy still listens on host port 5066.
  • The project listens on a random internal port.
  • ASPNETCORE_URLS points the project at the internal port, while browsers and other resources continue to use the stable proxy port.

If you omit the host port entirely, Aspire chooses random host and service ports for the endpoint:

apphost.mts
await builder.addProject("frontend", "../Networking_Frontend/Networking_Frontend.csproj")
.withHttpEndpoint();
Aspire frontend app networking diagram with random host port and proxy port.

In this configuration, Aspire assigns a random proxy port for the host-facing endpoint and a separate random internal port for the project.

Project resource endpoints follow default heuristics. Some endpoints are included in ASPNETCORE_URLS, some are published as HTTP/HTTPS_PORTS, and some are resolved from Kestrel configuration. Use WithEndpointsInEnvironment when you need to filter which endpoints are exposed through environment variables:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder.addProject("apiservice", "../Networking.ApiService/Networking.ApiService.csproj")
.withHttpsEndpoint()
.withHttpsEndpoint({ port: 19227, name: "admin" })
.withEndpointsInEnvironment(["https"]); // include only "https"; "admin" is excluded
await builder.build().run();

This keeps the admin endpoint out of the environment variables while still defining it in the application model.

For more background on bindings, proxies, and endpoint annotations, see Inner-loop networking.

The C# and TypeScript AppHosts differ in how they reference .NET projects:

  • C# AppHost: A ProjectReference in the AppHost .csproj causes the Aspire SDK to generate a Projects.* type during build. AddProject<Projects.ApiService>("api") uses that generated type to locate and launch the project. For the SDK-level build behavior behind this generation step, see Aspire SDK.

  • TypeScript AppHost: No ProjectReference is needed. addProject("api", "../ApiService/ApiService.csproj") points directly to the .csproj file. The TypeScript SDK resolves the project path at startup.

In a C# AppHost, if you need a regular project reference that shouldn’t become an Aspire resource, set IsAspireProjectResource="false" on the ProjectReference:

AppHost.csproj
<ProjectReference Include="..\MyProject\MyProject.csproj" IsAspireProjectResource="false" />

In a TypeScript AppHost, this concept doesn’t apply — you only add resources you explicitly call addProject on, so simply omit the addProject call.

In a C# AppHost, if multiple projects would generate the same Projects.* type name, set AspireProjectMetadataTypeName on the ProjectReference:

AppHost.csproj
<ItemGroup>
<ProjectReference Include="..\Microservice1\Presentation.Api\Presentation.Api.csproj"
AspireProjectMetadataTypeName="MicroService1" />
<ProjectReference Include="..\Microservice2\Presentation.Api\Presentation.Api.csproj"
AspireProjectMetadataTypeName="MicroService2" />
</ItemGroup>

Then reference those generated types explicitly:

The TypeScript AppHost doesn’t use generated project types. Use the .csproj path directly for each project — name collisions don’t apply:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const microservice1 = await builder.addProject("micro1", "../Microservice1/Presentation.Api/Presentation.Api.csproj");
const microservice2 = await builder.addProject("micro2", "../Microservice2/Presentation.Api/Presentation.Api.csproj");
await builder.build().run();

In a C# AppHost, use AddProject<T> when you have a standard .csproj, can add a ProjectReference from the AppHost, and want generated Projects.* types plus launch-profile integration. Use AddCSharpApp when you want to run a single .cs file, or when you want to point at a project path directly without adding a ProjectReference from the AppHost.

In a TypeScript AppHost, addProject(name, path) always takes a path, so the distinction between AddProject<T> and AddCSharpApp doesn’t apply in the same way.

Some .NET app types use specialized integrations instead of AddProject. For example, .NET MAUI integration uses AddMauiProject because MAUI apps aren’t added to the AppHost through ProjectReference metadata.