Dapr helps developers build event-driven, resilient distributed applications. Whether on-premises, in the cloud, or on an edge device, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.
This article will discuss how to configure the Mongo State Store component in .NET using Dapr.
With Dapr, we can now change the state store by putting in a different Dapr state store component with zero code change required to publish or consume the events.
Source Code: https://github.com/kshitijcode/dapr-adventures
Other Parts in Adventure with Dapr Series.
What are we going to build?
Architecture
SaveState Service: A .NET 5.0 service which has a /saveWeatherState endpoint to persist data into MongoDB via Dapr.
Prerequisites :
Dapr 1.2 is installed locally.
Visual Studio Code
MongoDB for VSCode Extension
Docker — To Install Local MongoDB
Provisioning the MongoDB Locally
docker run --name adventurous -p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password -d mongo
This will spin up a Local MongoDB with the specified credentials.
Setting up Dapr
Clone the repository to your local :
git clone git@github.com:kshitijcode/dapr-adventures.git
cd dapr-adventures
To get started, we will set up the state store component definition for Dapr to use our local MongoDB :
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: mongo
spec:
type: state.mongodb
version: v1
metadata:
- name: host
secretKeyRef:
name: MONGO_HOST
- name: username
secretKeyRef:
name: MONGO_ROOT_USERNAME
- name: password
secretKeyRef:
name: MONGO_ROOT_PASSWORD
scopes:
- savestate-service
auth:
secretStore: envvar-secret-store
The metadata in the component definition for state store is self-explanatory.
We need host,username and password to connect and authenticate against MongoDB.
The mongo.yaml component definition uses an environment secret store Dapr component for reference value for all the metadata. This Dapr secret store component uses a locally defined environment variable for authentication. The secret store component is defined as follows :
apiVersion: dapr.io/v1alpha
1kind: Component
metadata:
name: envvar-secret-store
namespace: default
spec:
type: secretstores.local.env
version: v1
Thus we will set the metadata spec as referenced in eventhub.yaml as environment variables :
export MONGO_HOST=localhost:27017
export MONGO_ROOT_USERNAME="admin"
export MONGO_ROOT_PASSWORD="password"
Let’s start the Dapr sidecar and observe logs :
dapr run --app-id savestate-service --components-path
components --app-port 5300 --dapr-grpc-port 5301 --dapr-http-port 5380 --log-level debug
ℹ️ Starting Dapr with id savestate-service. HTTP Port: 5480. gRPC Port: 5401
INFO[0000] starting Dapr Runtime -- version 1.2.2 -- commit 8aabc2c app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] log level set to: debug app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] metrics server started on :54815/ app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.metrics type=log ver=1.2.2
INFO[0000] standalone mode configured app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] app id: savestate-service app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] mTLS is disabled. Skipping certificate request and tls validation app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] local service entry announced: savestate-service -> 192.168.29.185:54819 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.contrib type=log ver=1.2.2
INFO[0000] Initialized name resolution to mdns app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] found component. name: envvar-secret-store, type: secretstores.local.env/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] found component. name: pubsub, type: pubsub.azure.eventhubs/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] found component. name: mongo, type: state.mongodb/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] loading component. name: envvar-secret-store, type: secretstores.local.env/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2INFO[0000] component loaded. name: envvar-secret-store, type: secretstores.local.env/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2DEBU[0000] loading component. name: mongo, type: state.mongodb/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] waiting for all outstanding components to be processed app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
WARN[0000] error initializing state store mongo (state.mongodb/v1): error in connecting to mongodb, host: localhost:27017 error: connection() : auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1": (AuthenticationFailed) Authentication failed. app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
FATA[0000] process component mongo error: error in connecting to mongodb, host: localhost:27017 error: connection() : auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1": (AuthenticationFailed) Authentication failed. app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
Line 17,18 — Dapr fails to load componentIn Line 17,18 we can observe that we are not able to authenticate against MongoDB while loading the MongoDB component and getting the following error :
WARN[0000] error initializing state store mongo (state.mongodb/v1): error in connecting to mongodb, host: localhost:27017 error: connection() : auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1": (AuthenticationFailed) Authentication failed. app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
How do we fix this?
On debugging further and going through Dapr Mongo Component contrib code, I realized we need to specify the database as well for authentication in the Dapr component definition :
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: mongo
spec:
type: state.mongodb
version: v1
metadata:
- name: host
secretKeyRef:
name: MONGO_HOST
- name: username
secretKeyRef:
name: MONGO_ROOT_USERNAME
- name: password
secretKeyRef:
name: MONGO_ROOT_PASSWORD
- name: databaseName
value: admin
scopes:
- savestate-service
auth:
secretStore: envvar-secret-store
If we don’t specify any value for databaseName it automatically defaults DB to daprStore as defined in component definition for Mongo StateStore.
Let’s rerun Dapr, and its starts successfully :
DEBU[0000] found component. name: mongo, type: state.mongodb/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] loading component. name: envvar-secret-store, type: secretstores.local.env/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] component loaded. name: envvar-secret-store, type: secretstores.local.env/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
DEBU[0000] loading component. name: mongo, type: state.mongodb/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] waiting for all outstanding components to be processed app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] component loaded. name: mongo, type: state.mongodb/v1 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] all outstanding components processed app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] enabled gRPC tracing middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.grpc.api type=log ver=1.2.2
INFO[0000] enabled gRPC metrics middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.grpc.api type=log ver=1.2.2
INFO[0000] API gRPC server is running on port 5401 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] enabled metrics http middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.http type=log ver=1.2.2
INFO[0000] enabled tracing http middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.http type=log ver=1.2.2
INFO[0000] http server is running on port 5480 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] The request body size parameter is: 4 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] enabled gRPC tracing middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.grpc.internal type=log ver=1.2.2
INFO[0000] enabled gRPC metrics middleware app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime.grpc.internal type=log ver=1.2.2
INFO[0000] internal gRPC server is running on port 55023 app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
INFO[0000] application protocol: http. waiting on port 5400. This will block until the app is listening on that port. app_id=savestate-service instance=Kshitijs-MacBook-Pro.local scope=dapr.runtime type=log ver=1.2.2
✅ You're up and running! Dapr logs will appear here.
Dapr Loads all components successfully.To read more on how to understand Dapr works and how to interpret logs kindly refer to Chapter 1 of the series.
SaveState Service
It’s a .NET 5 based service that runs on port 5300 and publishes an event on a POST request to /saveWeatherState endpoint.
Let us go over the controller for the service :
[HttpPost]
[Route("/saveWeatherState")]
public async Task<IActionResult> Post([FromBody] WeatherChange weatherChange)
{
try
{
await _daprClient.SaveStateAsync("mongo", "admin", weatherChange);
}
catch (Exception e)
{
return BadRequest();
}
return Ok();
}
We are using the Dapr Client .NET SDK to save data to Mongo via Dapr.
The first argument mongo is the name of the Dapr component that provides the state store implementation.
The second argument admin provides the name of the database to send the message.
The third argument is the payload of the message.
Open the solution in VSCode and run the Debug PublisherService Launch configuration from the debugger section.
The application starts running on port 5300. This is the same port Dapr listens to as defined by the app-port parameter while starting Dapr.
Microsoft.Hosting.Lifetime: Information: Now listening on: http://127.0.0.1:5300
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://127.0.0.1:5300
Microsoft.Hosting.Lifetime: Information: Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
Microsoft.Hosting.Lifetime: Information: Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /Users/sharmaks/microsoft/playground/dapr-adventures/DaprAdventures.SaveStateService
Microsoft.Hosting.Lifetime: Information: Content root path: /Users/sharmaks/microsoft/playground/dapr-adventures/DaprAdventures.SaveStateService
In Startup.cs, we have defined the Dapr gRPC and http endpoints for publishing events to Dapr:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddDapr(builder =>
builder.UseHttpEndpoint(Configuration.GetValue<string>
("DAPR_HTTP_ENDPOINT"))
.UseGrpcEndpoint(Configuration.GetValue<string>
("DAPR_GRPC_ENDPOINT")));
services.AddControllers();
services.AddSwaggerGen(c=>
{
c.SwaggerDoc("v1", newOpenApiInfo
{
Title = "DaprAdventures.SaveStateService",
Version = "v1"
});
});
}
The values are the same on which the Dapr sidecars runs as defined by dapr-grpc-port and dapr-http-port parameters:
{
"Logging":
{
"LogLevel":
{
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"DAPR_HTTP_ENDPOINT": "http://localhost:5380",
"DAPR_GRPC_ENDPOINT": "http://localhost:5301"
}
Line 10,11: Values for the PORT Endpoint
The SaveState Service is ready now.
We can send a payload by executing the savestate.rest sample in VSCode using the VSCode Rest Extension. You can try hitting the endpoint with payload through Curl or Postman as well.
View the Data
We can view the saved data by exec into the docker container or through the VSCode Mongo Extension. Connect to the MongoDB using the extension, and we can validate the data :
Connection to Local MongoDB
Actual Data Stored in Local Mongo
We can see the data getting persisted in the Local MongoDB.
Source: Medium - Kshitij Sharma
The Tech Platform
Comments