Skip to content

[DRAFT] Dapr content #894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
112 changes: 112 additions & 0 deletions docs/frameworks/dapr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
title: Use Dapr with .NET Aspire
description: Learn how to use Dapr with .NET Aspire
ms.date: 05/14/2024
ms.topic: overview
---

# Use Dapr with .NET Aspire

[Distributed Application Runtime (Dapr)](https://docs.dapr.io/) offers developer APIs that run as a sidecar process and abstract away the common complexities of the underlying cloud platform. Dapr and .NET Aspire work together to improve your local development experience. By using Dapr with .NET Aspire, you can focus on writing and implementing .NET-based distributed applications instead of spending extra time with local onboarding.

In this guide, you'll learn how to take advantage of Dapr's abstraction and .NET Aspire's opinionated configuration of cloud technologies to build simple, portable, resilient, and secured microservices at-scale on Azure.

[!INCLUDE [aspire-prereqs](../includes/aspire-prereqs.md)]

In addition to the prerequisites for .NET Aspire, you will need:

- Dapr version 1.13 or later

To install Dapr, see [Install the Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/). After installing the Dapr CLI, run the `dapr init` described in [Initialize Dapr in your local environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/).

## Get started

To get started you need to add the Dapr hosting package to your app host project by installing the [Aspire.Hosting.Dapr](https://www.nuget.org/packages/Aspire.Hosting.Dapr) NuGet package.

### [.NET CLI](#tab/dotnet-cli)

```dotnetcli
dotnet add package Aspire.Hosting.Dapr --prerelease
```

### [PackageReference](#tab/package-reference)

```xml
<PackageReference Include="Aspire.Hosting.Dapr"
Version="[SelectVersion]" />
```

---

For more information, see [dotnet add package](/dotnet/core/tools/dotnet-add-package) or [Manage package dependencies in .NET applications](/dotnet/core/tools/dependencies).

The Dapr resource is added to the .NET Aspire distributed application builder using the `AddDapr()` method.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now implicit when calling WithDaprSidecar(), so it's needed only when the user needs non-default behavior (such as explicitly specifying the path to the Dapr CLI).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An overload of the `AddDapr()` method that accepts Dapr options is available. For most applications, the default options will suffice.

:::code language="csharp" source="snippets/Dapr/Dapr.AppHost/Program.cs" range="4":::

Dapr uses the Sidecar pattern to run alongside your application. The Dapr sidecar is a lightweight, portable, and stateless HTTP server that listens for incoming HTTP requests from your application. The sidecar is responsible for managing the lifecycle of your application, including service discovery, configuration, and secrets management. To add a sidecar to a .NET Aspire resource by using the `WithDaprSidecar(string appId)` method. The `appId` parameter is the unique identifier for the Dapr application.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Dapr sidecar doesn't start/stop your application, so I'm not sure I'd say that it manages its "lifecycle" so much as it serves as a conduit for interacting with other services and dependencies and abstracts the application from the specifics of those services and dependencies.

Copy link
Contributor

@hhunter-ms hhunter-ms May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#939

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should lead with the WithDaprSidecar() overload as I believe that will infer the appId from the name given to the resource itself (which is probably what you'd want the appId to be as well).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NicoVermeir Would you be able to update your code to reflect this?


:::code language="csharp" source="snippets/Dapr/Dapr.AppHost/Program.cs" range="18-21" highlight="21":::

The `WithDaprSidecar` method offers overloads to configure your Dapr sidecar options like app ID and ports. In the following example, the Dapr sidecar is configured with specific ports for GRPC, HTTP, metrics and a specific App ID.

:::code language="csharp" source="snippets/Dapr/Dapr.AppHost/Program.cs" range="6-16" highlight="1-7,11":::

Putting everything together, here's an example of a .NET Aspire app host project which includes:

- A backend that declares a Dapr sidecar with specific ports and app ID.
- A frontend that declares a Dapr sidecar with a specific app ID and default ports.

:::code language="csharp" source="snippets/Dapr/Dapr.AppHost/Program.cs":::

The .NET Aspire dashboard will shows the Dapr sidecar as a resource, with its status and logs.

:::image type="content" source="media/aspire-dashboard-dapr-sidecar-resources.png" lightbox="media/aspire-dashboard-dapr-sidecar-resources.png" alt-text=".NET Aspire dashboard showing Dapr sidecar resources":::

## Adding the Dapr SDK

To use Dapr APIs from .NET Aspire resources you can use the [Dapr SDK for ASP.NET Core](https://www.nuget.org/packages/Dapr.AspNetCore/). The Dapr SDK provides a set of APIs to interact with Dapr sidecars.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note that the Dapr.AspNetCore library is for when wanting to use some of the Dapr integration with ASP.NET (DI integration, registration of subscriptions, etc.); non-ASP.NET apps (e.g. console apps) can just use the Dapr.Client library if they just want to make calls through the Dapr sidecar.

Copy link
Contributor

@hhunter-ms hhunter-ms May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#939


### [.NET CLI](#tab/dotnet-cli)

```dotnetcli
dotnet add package Dapr.AspNetCore
```

### [PackageReference](#tab/package-reference)

```xml
<PackageReference Include="Dapr.AspNetCore"
Version="[SelectVersion]" />
```

---

Once installed into an ASP.NET Core project, the SDK can be added to the service builder.

:::code language="csharp" source="snippets/Dapr/Dapr.Web/Program.cs" range="15":::

An instance of `DaprClient` can now be injected into your services to interact with the Dapr sidecar through the Dapr SDK.

:::code language="csharp" source="snippets/Dapr/Dapr.Web/WeatherApiClient.cs" highlight="9-10":::

`InvokeMethodAsync` is a method that sends an HTTP request to the Dapr sidecar. It is a generic method that takes an HTTP verb, the Dapr app ID of the service to call, the method name and a cancellation token. Depending on the HTTP verb it can also take a request body and headers. The generic type parameter is the type of the response body.

The full `Program.cs` file for the frontend project shows the Dapr Client being added to the service builder and the `WeatherApiClient` class that uses the Dapr Client to call the backend service.

:::code language="csharp" source="snippets/Dapr/Dapr.Web/Program.cs" highlight="15,17":::

In, for example, a Blazor project the `WeatherApiClient` class can be injected into a component and used to call the backend service.

:::code language="csharp" source="snippets/Dapr/Dapr.Web/Components/Pages/Weather.razor" highlight="5,47":::

What actually happens when the Dapr SDK is used is that the Dapr sidecar is called over HTTP. The Dapr sidecar then forwards the request to the target service. The target service can be running in the same process as the Dapr sidecar or in a different process. The Dapr sidecar is responsible for service discovery and routing the request to the target service.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the target service typically isn't running in the same process as the sidecar. Or, rather, the component related to a service may run in the Dapr sidecar, but that will forward calls (e.g. pub-sub, output bindings, etc.) to some other service.

Copy link
Contributor

@hhunter-ms hhunter-ms May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#939


## Dapr and .NET Aspire

At first sight Dapr and .NET Aspire may look like they have overlapping functionality, and they do. However, they both take a different approach. .NET Aspire is an opiniated approach on how to build distributed applications on a cloud platform. Dapr is a runtime that abstracts away the common complexities of the underlying cloud platform. It relies on sidecars to provide abstractions for things like configuration, secret management and messaging. The underlying technology can be easily switched out through configuration files, while your code does not need to change.

.NET Aspire makes setting up and debugging Dapr applications easier by providing a straightforward API to configure Dapr sidecars, and by exposing the sidecars as resources in the dashboard.

[.NET Aspire Dapr sample app](/samples/dotnet/aspire-samples/aspire-dapr/)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Dapr.ServiceDefaults\Dapr.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
39 changes: 39 additions & 0 deletions docs/frameworks/snippets/Dapr/Dapr.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var builder = WebApplication.CreateBuilder(args);

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddProblemDetails();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseExceptionHandler();

var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
});

app.MapDefaultEndpoints();

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5463",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:7487;http://localhost:5463",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
22 changes: 22 additions & 0 deletions docs/frameworks/snippets/Dapr/Dapr.AppHost/Dapr.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>0bd3d839-67a8-422d-aee2-bd9f1c0556a1</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Dapr.ApiService\Dapr.ApiService.csproj" />
<ProjectReference Include="..\Dapr.Web\Dapr.Web.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0-preview.7.24251.11" />
<PackageReference Include="Aspire.Hosting.Dapr" Version="8.0.0-preview.7.24251.11" />
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions docs/frameworks/snippets/Dapr/Dapr.AppHost/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Aspire.Hosting.Dapr;

var builder = DistributedApplication.CreateBuilder(args);
builder.AddDapr();

DaprSidecarOptions sidecarOptions = new ()
{
AppId = "apiservice-dapr",
DaprGrpcPort = 50001,
DaprHttpPort = 3500,
MetricsPort = 9090
};

var apiService = builder
.AddProject<Projects.Dapr_ApiService>("apiservice")
.WithDaprSidecar(sidecarOptions);

builder.AddProject<Projects.Dapr_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService)
.WithDaprSidecar("webfrontend-dapr");

builder.Build().Run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:17181;http://localhost:15032",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21181",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22225"
}
},
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:15032",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"DOTNET_ENVIRONMENT": "Development",
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19169",
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20292"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions docs/frameworks/snippets/Dapr/Dapr.AppHost/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Aspire.Hosting.Dcp": "Warning"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsAspireSharedProject>true</IsAspireSharedProject>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />

<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.3.0" />
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.7.24251.11" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
</ItemGroup>

</Project>
Loading
Loading