Getting Started With Dapr for Building Cloud-Native Microservices in .NET
A beginner-friendly guide to Dapr for .NET developers: learn sidecars, state, pub/sub, and service invocation to build cloud-native microservices.
Building one program is easy. Building many small programs that must talk to each other over a network is hard. Messages get lost. One service is slow. Another one restarts. Where do you store data? Which message queue do you use? These problems show up again and again, and every team solves them from scratch.
Dapr was made to stop that repeating pain. In this guide you will learn what Dapr is, why .NET developers like it, and how to use its main features with simple C# code. We will keep the language plain and the steps small.
A simple everyday analogy
Think about a busy tiffin (lunchbox) delivery service in a big city like Mumbai. The cook makes the food. But the cook does not personally know every road, every train, or every customer's address. Instead, each cook has a delivery helper standing right next to the kitchen. The cook just hands the tiffin to the helper and says "send this to Mr. Rao." The helper figures out the route, handles the train, retries if the door was locked, and brings back the empty box.
The cook focuses on cooking. The helper handles the messy delivery world.
Dapr is that delivery helper for your microservice. Your .NET app is the cook. It focuses on business logic. Dapr stands right beside it and handles the messy network world: finding other services, retrying failed calls, saving data, and sending messages. Your app just says "do this" and Dapr does the hard part.
This "helper standing beside the app" is called a sidecar. Remember that word. It is the heart of Dapr.
What exactly is Dapr?
Dapr stands for Distributed Application Runtime. It is free, open source, and a graduated project of the Cloud Native Computing Foundation (CNCF), which means it is trusted and battle-tested in real production systems.
Dapr gives you a set of ready-made building blocks for the common problems every microservice faces. You call these building blocks over a simple, standard API. Dapr then talks to the real tool behind the scenes (Redis, Kafka, Azure Service Bus, and so on). If you change the tool later, your code does not change. Only a small config file changes.
The key idea: your app talks to Dapr, not directly to the infrastructure. That swap of "real tool" for "standard API" is what makes Dapr powerful.
Why .NET developers like Dapr
There is a clean Dapr SDK for .NET. It feels like normal C#. You add a NuGet package, inject a DaprClient, and call methods. No deep network code.
Here is a quick comparison of life with and without Dapr.
| Problem | Without Dapr | With Dapr |
|---|---|---|
| Calling another service | Hard-code URLs, write retry code | One method call, retries built in |
| Saving state | Pick and wire a database SDK | One SaveStateAsync call |
| Sending messages | Learn a broker's SDK fully | One PublishEventAsync call |
| Changing the broker | Rewrite code | Edit a YAML file |
| Secrets | Custom secret loading | Standard secrets API |
A nice bonus: Dapr is not one of those .NET tools that recently moved to a paid commercial license. Some popular libraries like MediatR and MassTransit are now commercially licensed for many uses. Dapr stays free and open source. That matters when you plan budgets for a real project.
The building blocks you will use most
Dapr has many building blocks, but beginners use these four the most:
- Service invocation — call another service safely.
- State management — save and read key/value data.
- Publish & subscribe (pub/sub) — send and receive messages.
- Secrets — read passwords and keys safely.
The four starter building blocks
Steps
Invoke
Call another service
State
Save key/value data
PubSub
Send and get messages
Secrets
Read keys safely
Let's look at each one with code.
Setting up: install the tools
First, install the Dapr CLI and initialize Dapr on your machine. This sets up the local pieces (including a small Redis container for state and pub/sub).
// These are shell commands, shown here for clarity.
// 1. Install the Dapr CLI (see docs for your OS).
// 2. Initialize Dapr locally:
// dapr init
// 3. Add the .NET SDK to your ASP.NET Core project:
// dotnet add package Dapr.AspNetCoreIn Program.cs, register Dapr so you can inject DaprClient anywhere.
var builder = WebApplication.CreateBuilder(args);
// Adds DaprClient to dependency injection.
builder.Services.AddControllers().AddDapr();
var app = builder.Build();
// Lets Dapr deliver pub/sub messages and cloud events to your app.
app.UseCloudEvents();
app.MapSubscribeHandler();
app.MapControllers();
app.Run();Now you are ready to use the building blocks.
Building block 1: Service invocation
Imagine an Order service that needs to ask a Payment service to charge a customer. Without Dapr you would store the Payment URL, add retry loops, and handle service discovery. With Dapr you just name the other app.
public class OrderService
{
private readonly DaprClient _dapr;
public OrderService(DaprClient dapr) => _dapr = dapr;
public async Task<PaymentResult> ChargeAsync(PaymentRequest request)
{
// "payment-service" is the Dapr app-id of the other service.
// Dapr finds it, calls it, retries on failure, and returns the result.
return await _dapr.InvokeMethodAsync<PaymentRequest, PaymentResult>(
HttpMethod.Post,
"payment-service",
"charge",
request);
}
}Notice there is no URL. You only use the app-id ("payment-service"). Dapr handles discovery, load balancing, encryption between sidecars, and automatic retries. If the Payment service moves to a new server, your code does not change.
Building block 2: State management
Microservices often need to remember things: a shopping cart, a user session, a draft order. Dapr gives you a simple key/value store. Behind the scenes it might be Redis, Cosmos DB, or many others. Your code stays the same.
public class CartService
{
private readonly DaprClient _dapr;
private const string StoreName = "statestore";
public CartService(DaprClient dapr) => _dapr = dapr;
public async Task SaveCartAsync(string userId, Cart cart)
{
// Save the cart under a key. Dapr writes it to the configured store.
await _dapr.SaveStateAsync(StoreName, userId, cart);
}
public async Task<Cart?> GetCartAsync(string userId)
{
// Read it back by key. Returns null if not found.
return await _dapr.GetStateAsync<Cart>(StoreName, userId);
}
}The name "statestore" points to a small YAML component file. To switch from Redis to Cosmos DB, you edit that one file. The C# code never changes. That is the magic of the standard API.
Dapr state also supports useful extras you get "for free":
| Feature | What it gives you |
|---|---|
| Concurrency (ETags) | Stops two writers from overwriting each other |
| Consistency options | Choose strong or eventual consistency |
| Bulk operations | Read or write many keys at once |
| TTL (time to live) | Auto-delete data after a set time |
Building block 3: Publish & subscribe
Sometimes a service should not wait for an answer. When an order is placed, you may want to email the customer, update stock, and start shipping — all at once, without the order service waiting. This is where pub/sub shines. The order service publishes an event. Other services subscribe and react.
First, publishing an event:
public class OrderProcessor
{
private readonly DaprClient _dapr;
public OrderProcessor(DaprClient dapr) => _dapr = dapr;
public async Task PlaceOrderAsync(Order order)
{
// "pubsub" is the component name. "orders" is the topic name.
await _dapr.PublishEventAsync("pubsub", "orders", order);
// The order service is now done. It does not wait for subscribers.
}
}Now, another service subscribes to that topic. In ASP.NET Core you mark an endpoint with [Topic].
[ApiController]
public class ShippingController : ControllerBase
{
// Dapr delivers every "orders" message to this method.
[Topic("pubsub", "orders")]
[HttpPost("ship-order")]
public IActionResult OnOrderPlaced(Order order)
{
// Start packing and shipping here.
return Ok();
}
}The publisher and subscriber never know about each other. They only share a topic name. This keeps services loosely coupled, which is the whole point of good microservice design.
How a published event flows
Steps
Order
Publishes event
Topic
orders queue
Sends receipt
Stock
Reduces count
Shipping
Starts delivery
Building block 4: Secrets
Never put passwords or API keys directly in code or config files. Dapr gives you a standard secrets API. The real store can be a local file (for development), Azure Key Vault, HashiCorp Vault, or Kubernetes secrets.
public class ReportService
{
private readonly DaprClient _dapr;
public ReportService(DaprClient dapr) => _dapr = dapr;
public async Task<string> GetDbPasswordAsync()
{
// Reads "db-password" from the configured secret store.
var secret = await _dapr.GetSecretAsync("secretstore", "db-password");
return secret["db-password"];
}
}Same idea as before: your code asks Dapr, and Dapr talks to the real secret store. Move from a dev file to Azure Key Vault in production by editing one component file.
How a Dapr app runs locally
When you run a Dapr app, you do not run just your program. You run your program plus its sidecar. The Dapr CLI starts both together.
// Run your service together with its Dapr sidecar:
// dapr run --app-id order-service --app-port 5000 -- dotnet run
//
// --app-id : the name other services use to find you
// --app-port : the port your ASP.NET Core app listens on
// the part after -- : the command that starts your appHere is the picture of what runs on your machine.
Self-hosted vs Kubernetes
A common worry for beginners is: "Do I need Kubernetes?" No. Dapr runs in two main modes, and your application code is identical in both.
| Mode | Where it runs | When to use it |
|---|---|---|
| Self-hosted | Your laptop or a single VM | Learning, local development, small apps |
| Kubernetes | A real cluster in the cloud | Production, scaling, many services |
You write and test on your laptop in self-hosted mode. When you are ready, you deploy the same services to Kubernetes. Dapr automatically injects the sidecar into each pod. You did not rewrite anything. This "write once, run anywhere" feeling is a big reason teams pick Dapr.
Dapr and .NET Aspire work well together
If you have read about .NET Aspire, good news: it pairs nicely with Dapr. Aspire gives you a clean local dashboard and orchestrates your services, containers, and sidecars with a single command. You get logs, traces, and metrics in one place. Dapr handles the distributed plumbing; Aspire makes running and watching everything pleasant during development. Beginners do not need both on day one, but it is worth knowing they fit together when your project grows.
A small mental model to remember
When you feel lost, return to the tiffin analogy:
- Your app is the cook. It makes business decisions.
- The sidecar is the delivery helper. It does the network work.
- Building blocks are the helper's skills: deliver (invoke), store (state), broadcast (pub/sub), and fetch keys (secrets).
- Component files say which real tool the helper uses. Swap the tool, keep the code.
If you hold this picture, every Dapr feature will make sense.
Common beginner mistakes to avoid
- Forgetting the sidecar. If you run
dotnet runalone, Dapr is not there. Always start withdapr run. - Hard-coding store names in many places. Put names like
"statestore"in one constant or config value. - Treating pub/sub like a request/response call. Publishing is fire-and-forget. If you need an answer back, use service invocation instead.
- Skipping secrets. It is tempting to paste a key into config. Use the secrets API from the start; it costs little and saves you later.
Quick recap
- Dapr is a free, open-source runtime that handles the hard parts of microservices for you.
- It runs as a sidecar: a helper beside your app, like a tiffin delivery helper beside a cook.
- Your .NET app talks to Dapr through simple APIs, not directly to infrastructure.
- The four starter building blocks are service invocation, state management, pub/sub, and secrets.
- You swap the real tool (Redis, Kafka, Key Vault) by editing a component file, not your code.
- Dapr runs the same way in self-hosted mode on your laptop and in Kubernetes in production.
- Dapr stays free while some other .NET libraries have moved to paid commercial licenses.
- Start with
dapr run, injectDaprClient, and call one method at a time.
References and further reading
- Getting started with the Dapr client .NET SDK — Dapr Docs
- Dapr for .NET Developers — Microsoft Learn
- Dapr official website
- Introduction to Dapr for .NET Developers — Milan Jovanović
- Getting Started With Dapr for Building Cloud-Native Microservices in .NET — AntonDevTips
Related Posts
.NET Aspire: A Game Changer for Cloud-Native Development
A beginner-friendly guide to .NET Aspire, the cloud-native stack that orchestrates your services, databases, and dashboards with one simple command.
Containerize Your .NET Applications Without a Dockerfile
Learn how to build container images for your .NET apps using the SDK and dotnet publish, with no Dockerfile needed. Beginner-friendly guide for .NET 10.
Introduction to Dapr for .NET Developers: A Beginner Guide
A warm, beginner-friendly introduction to Dapr for .NET developers, covering sidecars, building blocks, state, pub/sub, and service invocation in plain C#.
Service Discovery in .NET Microservices with HashiCorp Consul
A beginner-friendly guide to service discovery in .NET microservices using HashiCorp Consul, with registration, health checks, and lookups explained simply.
How .NET Aspire Simplifies Service Discovery for Your Apps
Learn how .NET Aspire service discovery lets your services find each other by name, with no hardcoded URLs, ports, or environment headaches.
Structured Logging and Distributed Tracing for Microservices with Seq
Learn to add structured logging with Serilog and distributed tracing with OpenTelemetry to .NET microservices, then view it all in Seq with one trace ID.