DaprClient usage
Lifetime management
A DaprClient
holds access to networking resources in the form of TCP sockets used to communicate with the Dapr sidecar. DaprClient
implements IDisposable
to support eager cleanup of resources.
Dependency Injection
The AddDaprClient()
method will register the Dapr client with ASP.NET Core dependency injection. This method accepts an optional
options delegate for configuring the DaprClient
and an ServiceLifetime
argument, allowing you to specify a different lifetime
for the registered resources instead of the default Singleton
value.
The following example assumes all default values are acceptable and is sufficient to register the DaprClient
.
services.AddDaprClient();
The optional configuration delegates are used to configure DaprClient
by specifying options on the provided DaprClientBuilder
as in the following example:
services.AddDaprClient(daprBuilder => {
daprBuilder.UseJsonSerializerOptions(new JsonSerializerOptions {
WriteIndented = true,
MaxDepth = 8
});
daprBuilder.UseTimeout(TimeSpan.FromSeconds(30));
});
The another optional configuration delegate overload provides access to both the DaprClientBuilder
as well as an IServiceProvider
allowing for more advanced configurations that may require injecting services from the dependency injection container.
services.AddSingleton<SampleService>();
services.AddDaprClient((serviceProvider, daprBuilder) => {
var sampleService = serviceProvider.GetRequiredService<SampleService>();
var timeoutValue = sampleService.TimeoutOptions;
daprBuilder.UseTimeout(timeoutValue);
});
Manual Instantiation
Rather than using dependency injection, a DaprClient
can also be built using the static client builder.
For best performance, create a single long-lived instance of DaprClient
and provide access to that shared instance throughout your application. DaprClient
instances are thread-safe and intended to be shared.
Avoid creating a DaprClient
per-operation and disposing it when the operation is complete.
Configuring DaprClient
A DaprClient
can be configured by invoking methods on DaprClientBuilder
class before calling .Build()
to create the client. The settings for each DaprClient
object are separate and cannot be changed after calling .Build()
.
var daprClient = new DaprClientBuilder()
.UseJsonSerializerSettings( ... ) // Configure JSON serializer
.Build();
By default, the DaprClientBuilder
will prioritize the following locations, in the following order, to source the configuration
values:
- The value provided to a method on the
DaprClientBuilder
(e.g.UseTimeout(TimeSpan.FromSeconds(30))
) - The value pulled from an optionally injected
IConfiguration
matching the name expected in the associated environment variable - The value pulled from the associated environment variable
- Default values
Configuring on DaprClientBuilder
The DaprClientBuilder
contains the following methods to set configuration options:
UseHttpEndpoint(string)
: The HTTP endpoint of the Dapr sidecarUseGrpcEndpoint(string)
: Sets the gRPC endpoint of the Dapr sidecarUseGrpcChannelOptions(GrpcChannelOptions)
: Sets the gRPC channel options used to connect to the Dapr sidecarUseHttpClientFactory(IHttpClientFactory)
: Configures the DaprClient to use a registeredIHttpClientFactory
when buildingHttpClient
instancesUseJsonSerializationOptions(JsonSerializerOptions)
: Used to configure JSON serializationUseDaprApiToken(string)
: Adds the provided token to every request to authenticate to the Dapr sidecarUseTimeout(TimeSpan)
: Specifies a timeout value used by theHttpClient
when communicating with the Dapr sidecar
Configuring From IConfiguration
Rather than rely on sourcing configuration values directly from environment variables or because the values are sourced
from dependency injected services, another options is to make these values available on IConfiguration
.
For example, you might be registering your application in a multi-tenant environment and need to prefix the environment
variables used. The following example shows how these values can be sourced from the environment variables to your
IConfiguration
when their keys are prefixed with test_
;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddEnvironmentVariables("test_"); //Retrieves all environment variables that start with "test_" and removes the prefix when sourced from IConfiguration
builder.Services.AddDaprClient();
Configuring From Environment Variables
The SDK will read the following environment variables to configure the default values:
DAPR_HTTP_ENDPOINT
: used to find the HTTP endpoint of the Dapr sidecar, example:https://dapr-api.mycompany.com
DAPR_GRPC_ENDPOINT
: used to find the gRPC endpoint of the Dapr sidecar, example:https://dapr-grpc-api.mycompany.com
DAPR_HTTP_PORT
: ifDAPR_HTTP_ENDPOINT
is not set, this is used to find the HTTP local endpoint of the Dapr sidecarDAPR_GRPC_PORT
: ifDAPR_GRPC_ENDPOINT
is not set, this is used to find the gRPC local endpoint of the Dapr sidecarDAPR_API_TOKEN
: used to set the API Token
Note
If bothDAPR_HTTP_ENDPOINT
and DAPR_HTTP_PORT
are specified, the port value from DAPR_HTTP_PORT
will be ignored in favor of the port
implicitly or explicitly defined on DAPR_HTTP_ENDPOINT
. The same is true of both DAPR_GRPC_ENDPOINT
and DAPR_GRPC_PORT
.
Configuring gRPC channel options
Dapr’s use of CancellationToken
for cancellation relies on the configuration of the gRPC channel options and this is enabled by default. If you need to configure these options yourself, make sure to enable the ThrowOperationCanceledOnCancellation setting.
var daprClient = new DaprClientBuilder()
.UseGrpcChannelOptions(new GrpcChannelOptions { ... ThrowOperationCanceledOnCancellation = true })
.Build();
Using cancellation with DaprClient
The APIs on DaprClient that perform asynchronous operations accept an optional CancellationToken
parameter. This follows a standard .NET idiom for cancellable operations. Note that when cancellation occurs, there is no guarantee that the remote endpoint stops processing the request, only that the client has stopped waiting for completion.
When an operation is cancelled, it will throw an OperationCancelledException
.
Understanding DaprClient JSON serialization
Many methods on DaprClient
perform JSON serialization using the System.Text.Json
serializer. Methods that accept an application data type as an argument will JSON serialize it, unless the documentation clearly states otherwise.
It is worth reading the System.Text.Json documentation if you have advanced requirements. The Dapr .NET SDK provides no unique serialization behavior or customizations - it relies on the underlying serializer to convert data to and from the application’s .NET types.
DaprClient
is configured to use a serializer options object configured from JsonSerializerDefaults.Web. This means that DaprClient
will use camelCase
for property names, allow reading quoted numbers ("10.99"
), and will bind properties case-insensitively. These are the same settings used with ASP.NET Core and the System.Text.Json.Http
APIs, and are designed to follow interoperable web conventions.
System.Text.Json
as of .NET 5.0 does not have good support for all of F# language features built-in. If you are using F# you may want to use one of the converter packages that add support for F#’s features such as FSharp.SystemTextJson.
Simple guidance for JSON serialization
Your experience using JSON serialization and DaprClient
will be smooth if you use a feature set that maps to JSON’s type system. These are general guidelines that will simplify your code where they can be applied.
- Avoid inheritance and polymorphism
- Do not attempt to serialize data with cyclic references
- Do not put complex or expensive logic in constructors or property accessors
- Use .NET types that map cleanly to JSON types (numeric types, strings,
DateTime
) - Create your own classes for top-level messages, events, or state values so you can add properties in the future
- Design types with
get
/set
properties OR use the supported pattern for immutable types with JSON
Polymorphism and serialization
The System.Text.Json
serializer used by DaprClient
uses the declared type of values when performing serialization.
This section will use DaprClient.SaveStateAsync<TValue>(...)
in examples, but the advice is applicable to any Dapr building block exposed by the SDK.
public class Widget
{
public string Color { get; set; }
}
...
// Storing a Widget value as JSON in the state store
widget widget = new Widget() { Color = "Green", };
await client.SaveStateAsync("mystatestore", "mykey", widget);
In the example above, the type parameter TValue
has its type argument inferred from the type of the widget
variable. This is important because the System.Text.Json
serializer will perform serialization based on the declared type of the value. The result is that the JSON value { "color": "Green" }
will be stored.
Consider what happens when you try to use derived type of Widget
:
public class Widget
{
public string Color { get; set; }
}
public class SuperWidget : Widget
{
public bool HasSelfCleaningFeature { get; set; }
}
...
// Storing a SuperWidget value as JSON in the state store
Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, };
await client.SaveStateAsync("mystatestore", "mykey", widget);
In this example we’re using a SuperWidget
but the variable’s declared type is Widget
. Since the JSON serializer’s behavior is determined by the declared type, it only sees a simple Widget
and will save the value { "color": "Green" }
instead of { "color": "Green", "hasSelfCleaningFeature": true }
.
If you want the properties of SuperWidget
to be serialized, then the best option is to override the type argument with object
. This will cause the serializer to include all data as it knows nothing about the type.
Widget widget = new SuperWidget() { Color = "Green", HasSelfCleaningFeature = true, };
await client.SaveStateAsync<object>("mystatestore", "mykey", widget);
Error handling
Methods on DaprClient
will throw DaprException
or a subclass when a failure is encountered.
try
{
var widget = new Widget() { Color = "Green", };
await client.SaveStateAsync("mystatestore", "mykey", widget);
}
catch (DaprException ex)
{
// handle the exception, log, retry, etc.
}
The most common cases of failure will be related to:
- Incorrect configuration of Dapr component
- Transient failures such as a networking problem
- Invalid data, such as a failure to deserialize JSON
In any of these cases you can examine more exception details through the .InnerException
property.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.