Distributed Application Runtime (Dapr): Quick start on .NET 7

Anvar Nurmatov
4 min readNov 15, 2022

--

Distributed Application Runtime (Dapr)

Here is a quick tutorial on how to start working with Dapr from dev’s perspective. Dapr is supported by an ever-growing community and has comprehensive documentation. I wouldn’t explain how Dapr works in this post however your understanding of Dapr at least superficial is required. So how to get to it quickly?

Environment:
- MacBook M1 Pro (arm64)
- Visual Studio Code + Dapr extension + C# extension (powered by OmniSharp)
- .NET SDK
- Dapr .NET SDK (Dapr SDK languages)
- Docker Desktop

We’ll start with a simple webapi application with the following capacity:
- A “Hello world” HTTP endpoint
- Publishing to RabbitMQ
- Subscribing to RabbitMQ

But let’s install self-hosted Dapr on our hosting platform beforehand using dapr cli:

# this command will download all required binaries for daprd to operate properly
dapr init --slim

slim argument allows us to exclude unnecessary containers like placement service, Redis, and Zipkin from self-hosted installation, they can be added at any time later

Now let’s provision our docker with RabbitMQ container:

# this command will download an image if not exist, run container and forward ports 15672 (management) and 5672 (AMQP)
docker run -d --name dapr_rabbitmq -p 15672:15672 -p 5672:5672 arm64v8/rabbitmq:3-management

I opted-in an image specifically for arm64 (feel free to choose the one that suits your CPU architecture)

We’re almost done with preparation and now we need to create a webapi project:

# create a webapi project based on webapi template
dotnet new webapi -n DaprAppSample -minimal --no-https

When we open the project in VScode for the first time we’re asked to create build & debug assets or alternatively, we can do this by:

Let’s run the project (F5) and see if it’s working (the project is pre-added with a /weatherforecast endpoint).

Dapr integration into project

Now, when we have created the project and installed Dapr, is the time to tie them up. For that let’s adjust our build & debug assets.

Adding two tasks that are needed for running our application properly in Dapr runtime:

# tasks.json
{
"version": "2.0.0",
"tasks": [
...
{
"appId": "daprappsampleid",
"appPort": 5001,
"label": "dapr-debug",
"type": "dapr",
"dependsOn": "build",
"appProtocol": "http",
"httpPort": 6001,
"grpcPort": 6002,
// Dapr component path is for adding our dapr configuration files
"componentsPath": "./${workspaceFolder}/daprcomponents"
},
{
"appId": "daprappsampleid",
"label": "daprd-down",
"type": "daprd-down"
}
]
}

Modifying the launching configuration that will perform these two tasks before running application:

{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
// "build" changed to "dapr-debug"
"preLaunchTask": "dapr-debug",
// post debug task
"postDebugTask": "daprd-down",
"program": "${workspaceFolder}/DaprAppSample/bin/Debug/net7.0/DaprAppSample.dll",
"args": ["--urls","http://localhost:5001/"],
"cwd": "${workspaceFolder}/DaprAppSample",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
// dapr specific env vars
"DAPR_HTTP_PORT": "6001",
"DAPR_GRPC_PORT": "6002"
}
}
]
}

Adding Dapr components by creating a new folder daprcomponents in the working directory and creating a pubsub.yaml file inside:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
version: v1
type: pubsub.rabbitmq
metadata:
- name: host
value: "amqp://guest:guest@localhost:5672"

NOTE: There is a Dapr extension for VScode that can help us with the scaffolding of Dapr tasks and components

NOTE: If an IDE does not support such a pre-launch task it’s just required to run a side-car separately (before running an app) by:

dapr run --app-id daprappsampleid --app-port 5001 --app-protocol http --components-path ./DaprComponents --dapr-grpc-port 6002 --dapr-http-port 6001

That’s it, and now if we run our application it’ll be running in Dapr runtime. Little left to do: just adding package references and publishing/consuming our first message:

<PackageReference Include="Dapr.Client" Version="1.9.0" />
<PackageReference Include="Dapr.AspNetCore" Version="1.9.0" />

And eventually, there is a program.cs file:

using Dapr;
using Dapr.Client;

var builder = WebApplication.CreateBuilder(args);

// Dapr DI
builder.Services.AddDaprClient();

var app = builder.Build();

// Dapr cloud event middleware unwrap requests with cloud events structure
app.UseCloudEvents();
// Maps an endpoint that will respond to requests to /dapr/subscribe
app.MapSubscribeHandler();

app.MapGet("/hello", () => {
var daprClient = app.Services.GetRequiredService<DaprClient>();
daprClient.PublishEventAsync("pubsub",
"message",
new HelloRecord(Guid.NewGuid(),"Hello Dapr!"));
return Results.Ok("Hello Dapr!");
});

// an endpoint that will process messages consumed by pubsub
app.MapPost("/checkout", [Topic("pubsub","message")] (HelloRecord record) => {
Console.WriteLine($"Received a record with ID: {record.Id} and Text: {record.Message}");
return Results.Ok();
});

app.Run();

record HelloRecord(Guid Id, string Message);

This is a programmatical sample of the subscription, tbh I’d prefer better a declarative way using .yaml configurations.

GitHub repo: https://github.com/anurmatov/dapr-samples

I’m going to expand on the Dapr topic and have a series of articles soon to consolidate my knowledge about Dapr.

--

--