Table of Contents

Dapr cache

The Dapr kind stores the values in a Dapr state store. Dapr talks to many backends (Redis, PostgreSQL, Azure Cosmos DB and others), so the choice of the store moves out of your application and into a Dapr component file. This page extends the caching guide.

What it solves

DaprCache (DaprCache) is an ICache on top of the DaprClient of the Dapr.Client package. It exposes a state store as a named Arc4u cache, next to caches of the other kinds.

Compared with the other kinds:

  • Values are serialized by the Dapr client (System.Text.Json), not by an IObjectSerialization. There is no SerializerName, and you do not need to register a serializer for this kind.
  • A sliding expiration is not supported: Put with isSlided: true throws NotSupportedException.
  • Expiration is the ttlInSeconds metadata of the state store, so the state store must support time to live.

Packages involved

Package Use it for
Arc4u.Caching.Dapr DaprCache (kind Dapr) and AddDaprCacheKind(). References Dapr.Client.
Arc4u.Caching ICacheContext and AddCacheContext.

Install

dotnet add package Arc4u.Caching.Dapr --prerelease

The application must run with a Dapr sidecar (dapr run, or the Dapr annotations on Kubernetes) and a state store component whose metadata.name is the value you put in Settings:Name. The DaprClient finds the sidecar with the DAPR_HTTP_PORT and DAPR_GRPC_PORT environment variables that the sidecar sets.

Configuration

appsettings.json

{
  "Caching": {
    "Default": "Sidecar",
    "Caches": [
      {
        "Name": "Sidecar",
        "Kind": "Dapr",
        "IsAutoStart": true,
        "Settings": {
          "Name": "statestore"
        }
      }
    ]
  }
}

The settings bind to DaprCacheOption:

Key Type Default Description
Caching:Caches:n:Settings:Name string empty Name of the Dapr state store component, as in the metadata.name of its YAML file. Required in practice: an empty name makes every operation fail.

The state store component is Dapr configuration, not Arc4u configuration. A minimal file for a local test, using the in-memory state store of Dapr:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.in-memory
  version: v1

Use a persistent store, such as state.redis, in production. See the Dapr state store reference.

Code

// Program.cs
using Arc4u.Caching;
using Arc4u.Caching.Dapr;
using Arc4u.Dependency;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddILogger();
builder.Services.AddCacheContext(builder.Configuration);
builder.Services.AddDaprCacheKind();

var app = builder.Build();
app.Run();

Common scenarios

Native AOT and trimming

The Dapr client serializes with its default options, which use reflection. In a trimmed or native AOT application, give it options whose TypeInfoResolver is a JsonSerializerContext declaring the types you put in the cache. The name of the cache (Shared below) is the Name of the entry in Caching:Caches; the store name still comes from the configuration.

// Program.cs
using System.Text.Json;
using System.Text.Json.Serialization;
using Arc4u.Configuration.Dapr;

builder.Services.Configure<DaprCacheOption>("Shared", o =>
    o.JsonSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web) { TypeInfoResolver = AppJsonContext.Default });

[JsonSerializable(typeof(Report))]
internal sealed partial class AppJsonContext : JsonSerializerContext;

Keep JsonSerializerDefaults.Web to read values saved with the default options. A type that is not in the context throws a DataCacheException whose inner exception is a NotSupportedException.

Expire a value

using Arc4u.Caching;

public static class DaprExpiration
{
    public static Task RunAsync(ICache cache)
    {
        // Absolute expiration only: the ttlInSeconds metadata is sent to the state store.
        return cache.PutAsync("report", TimeSpan.FromMinutes(5), "value");
    }
}

Extensibility points

AddDaprCacheKind() registers DaprCache under the key Dapr with TryAddKeyedTransient. To replace it, register your own ICache under that key before calling it, or instead of it.

Troubleshooting

Operations fail to connect

No sidecar is reachable. Start the application with dapr run, or check the DAPR_HTTP_PORT and DAPR_GRPC_PORT variables. The initialization creates the client but does not contact the sidecar, so the error appears on the first operation.

DataCacheException on Get or Put

The Dapr client failed; its exception is the InnerException. The synchronous methods wait for the asynchronous Dapr calls, so prefer GetAsync and PutAsync in a web application.

See also