【C#】在.NET Aspire 中使用 Dapr 状态管理(State management)
一、AppHost项目
statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
Component | type |
---|---|
In-memory | state.in-memory |
Redis | state.redis |
SQLite | state.sqlite |
在AppHost项目添加组件:
var stateStore = builder.AddDaprComponent("statestore", "state.redis",
new DaprComponentOptions
{
LocalPath = "Resources\\statestore.yaml"
});
在其他项目中添加引用:
builder.AddProject<Projects.Aspire_WPF>("wpfclient")
// ...
.WithReference(stateStore) // 添加引用
// ...
二、使用方法
private const string storeName = "statestore";
1、设置值
await _client.SaveStateAsync(storeName, "key1", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString(), cancellationToken: cts.Token);
2、读取值
var state = await _client.GetStateAsync<long>(storeName, "key1", cancellationToken: cts.Token);
3、删除键
await _client.DeleteStateAsync(storeName, "key1", cancellationToken: cts.Token);
三、其它方法
Binary数据
写二进制数据
var stateBytes = Encoding.UTF8.GetBytes(state);
await client.SaveByteStateAsync(storeName, stateKeyName, stateBytes.AsMemory(), cancellationToken: cancellationToken);
读二进制数据
var responseBytes = await client.GetByteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken);
var savedState = Encoding.UTF8.GetString(ByteString.CopyFrom(responseBytes.Span).ToByteArray());
ETag使用
保存状态,每次保存都会更新 ETag (SaveStateAsync
)
await client.SaveStateAsync<Widget>(storeName, stateKeyName, new Widget() { Size = "small", Color = "yellow", }, cancellationToken: cancellationToken);
读取状态和ETag (GetStateAndETagAsync
)
var (original, originalETag) = await client.GetStateAndETagAsync<Widget>(storeName, stateKeyName, cancellationToken: cancellationToken);
Console.WriteLine($"Retrieved state: {original.Size} {original.Color} with etag: {originalETag}");
使用 ETag 来更新状态 (TrySaveStateAsync
)
original.Color = "purple";
var isSaveStateSuccess = await client.TrySaveStateAsync<Widget>(storeName, stateKeyName, original, originalETag, cancellationToken: cancellationToken);
Console.WriteLine($"Saved state with old etag: {originalETag} successfully? {isSaveStateSuccess}");
注意:ETag 是否有效 。每次 SaveStateAsync 后都会更新ETag,原ETag将失效。
使用 ETag 来删除状态(TryDeleteStateAsync
)
var isDeleteStateSuccess = await client.TryDeleteStateAsync(storeName, stateKeyName, originalETag, cancellationToken: cancellationToken);
Console.WriteLine($"Deleted state with old etag: {originalETag} successfully? {isDeleteStateSuccess}");
批量操作
批量存
var state1 = new Widget() { Size = "small", Color = "yellow", };
var state2 = new Widget() { Size = "big", Color = "green", };
var stateItem1 = new SaveStateItem<Widget>(firstKey, state1, firstEtag);
var stateItem2 = new SaveStateItem<Widget>(secondKey, state2, secondEtag);
await client.SaveBulkStateAsync(storeName, new List<SaveStateItem<Widget>>() { stateItem1, stateItem2});
指量取
IReadOnlyList<BulkStateItem> states = await client.GetBulkStateAsync(storeName,
new List<string>(){firstKey, secondKey}, null);
批量删
var deleteBulkStateItem1 = new BulkDeleteStateItem(states[0].Key, states[0].ETag);
var deleteBulkStateItem2 = new BulkDeleteStateItem(states[1].Key, states[1].ETag);
await client.DeleteBulkStateAsync(storeName, new List<BulkDeleteStateItem>() { deleteBulkStateItem1, deleteBulkStateItem2 });
事务操作
var bytes = JsonSerializer.SerializeToUtf8Bytes(value); // System.Text.Json
// var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)); // Newtonsoft.Json
var requests = new List<StateTransactionRequest>()
{
new StateTransactionRequest("widget", bytes, StateOperationType.Upsert),
new StateTransactionRequest("oldwidget", null, StateOperationType.Delete)
};
await client.ExecuteStateTransactionAsync(storeName, requests, cancellationToken: cancellationToken);
StateOperationType
- Upsert :插入或更新状态
- Delete:删除状态
四、附配置文件
详情:docs.dapr.io/reference/components-reference/supported-state-stores/
In-Memory
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: <NAME>
spec:
type: state.in-memory
version: v1
metadata:
# Uncomment this if you wish to use In-memory as a state store for actors (optional)
#- name: actorStateStore
# value: "true"
Sqlite
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: <NAME>
spec:
type: state.sqlite
version: v1
metadata:
# Connection string
- name: connectionString
value: "data.db"
# Timeout for database operations, in seconds (optional)
#- name: timeoutInSeconds
# value: 20
# Name of the table where to store the state (optional)
#- name: tableName
# value: "state"
# Cleanup interval in seconds, to remove expired rows (optional)
#- name: cleanupInterval
# value: "1h"
# Set busy timeout for database operations
#- name: busyTimeout
# value: "2s"
# Uncomment this if you wish to use SQLite as a state store for actors (optional)
#- name: actorStateStore
# value: "true"