top of page

What is Dapr ? Saving to Dapr MongoDB State Store Component in .NET 5.0



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.

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

0 comments
bottom of page