Client
Dapr 客户端包允许你从 .NET 应用程序与其他 Dapr 应用程序进行交互。
注意
如果尚未尝试,请尝试快速入门之一,快速了解如何使用 API 构建块结合 Dapr .NET SDK。构建块
.NET SDK 允许你与所有 Dapr 构建块 进行交互。
注意
我们只会在第一个示例(服务调用)中包含DaprClient 的依赖注入注册。在几乎所有其他示例中,我们假设你已经在后续示例的应用程序中注册了 DaprClient,并且已将 DaprClient 的实例作为名为 client 的实例注入到代码中。调用服务
HTTP
你可以使用 DaprClient 或 System.Net.Http.HttpClient 来调用服务。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDaprClient();
var app = builder.Build();
using var scope = app.Services.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<DaprClient>();
// 调用名为 "deposit" 的 POST 方法,该方法接受类型为 "Transaction" 的输入
var data = new { id = "17", amount = 99m };
var account = await client.InvokeMethodAsync<Account>("routing", "deposit", data, cancellationToken);
Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance);
using Microsoft.Extensins.Hosting; using Microsoft.Extensions.DependencyInjection;
var builder = Host.CreateApplicationBuilder(args); builder.Services.AddDaprClient(); var app = builder.Build();
using var scope = app.Services.CreateScope();
var client = scope.ServiceProvider.GetRequiredService
// 调用名为 “deposit” 的 POST 方法,该方法接受类型为 “Transaction” 的输入
var data = new { id = “17”, amount = 99m };
var account = await client.InvokeMethodAsync
var client = DaprClient.CreateInvokeHttpClient(appId: "routing");
// 在 HTTP 客户端上设置超时:
client.Timeout = TimeSpan.FromSeconds(2);
var deposit = new Transaction { Id = "17", Amount = 99m };
var response = await client.PostAsJsonAsync("/deposit", deposit, cancellationToken);
var account = await response.Content.ReadFromJsonAsync<Account>(cancellationToken: cancellationToken);
Console.WriteLine("Returned: id:{0} | Balance:{1}", account.Id, account.Balance);
gRPC
你可以使用 DaprClient 通过 gRPC 调用服务。
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));
var invoker = DaprClient.CreateInvocationInvoker(appId: myAppId, daprEndpoint: serviceEndpoint);
var client = new MyService.MyServiceClient(invoker);
var options = new CallOptions(cancellationToken: cts.Token, deadline: DateTime.UtcNow.AddSeconds(1));
await client.MyMethodAsync(new Empty(), options);
Assert.Equal(StatusCode.DeadlineExceeded, ex.StatusCode);
- 有关服务调用的完整指南,请访问操作指南:调用服务。
保存和获取应用程序状态
var state = new Widget() { Size = "small", Color = "yellow", };
await client.SaveStateAsync(storeName, stateKeyName, state, cancellationToken: cancellationToken);
Console.WriteLine("Saved State!");
state = await client.GetStateAsync<Widget>(storeName, stateKeyName, cancellationToken: cancellationToken);
Console.WriteLine($"Got State: {state.Size} {state.Color}");
await client.DeleteStateAsync(storeName, stateKeyName, cancellationToken: cancellationToken);
Console.WriteLine("Deleted State!");
查询状态(Alpha)
var query = "{" +
"\"filter\": {" +
"\"EQ\": { \"value.Id\": \"1\" }" +
"}," +
"\"sort\": [" +
"{" +
"\"key\": \"value.Balance\"," +
"\"order\": \"DESC\"" +
"}" +
"]" +
"}";
var queryResponse = await client.QueryStateAsync<Account>("querystore", query, cancellationToken: cancellationToken);
Console.WriteLine($"Got {queryResponse.Results.Count}");
foreach (var account in queryResponse.Results)
{
Console.WriteLine($"Account: {account.Data.Id} has {account.Data.Balance}");
}
- 有关状态操作的完整列表,请访问操作指南:获取和保存状态。
发布消息
var eventData = new { Id = "17", Amount = 10m, };
await client.PublishEventAsync(pubsubName, "deposit", eventData, cancellationToken);
Console.WriteLine("Published deposit event!");
- 有关状态操作的完整列表,请访问操作指南:发布和订阅。
- 访问 .NET SDK 示例 获取代码示例和试用发布订阅的说明
与输出绑定交互
调用 InvokeBindingAsync 时,你可以选择自己处理序列化和编码,或者让 SDK 为你将其序列化为 JSON 然后编码为字节。
重要
绑定所期望的数据形状各不相同,请特别注意并确保你发送的数据得到相应处理。如果你正在编写输出和输入,请确保它们都遵循相同的序列化约定。手动序列化
对于大多数场景,建议使用 InvokeBindingAsync 的此重载,因为它为你提供了清晰度和对数据处理方式的控制。
在此示例中,数据作为字符串的 UTF-8 字节表示发送。
using var client = new DaprClientBuilder().Build();
var request = new BindingRequest("send-email", "create")
{
// 注意:这是 Twilio SendGrid 绑定的示例负载
Data = Encoding.UTF8.GetBytes("<h1>Testing Dapr Bindings</h1>This is a test.<br>Bye!"),
Metadata =
{
{ "emailTo", "customer@example.com" },
{ "subject", "An email from Dapr SendGrid binding" },
},
}
await client.InvokeBindingAsync(request);
自动序列化和编码
在此示例中,数据作为序列化为 JSON 的值的 UTF-8 编码字节表示发送。
using var client = new DaprClientBuilder().Build();
var email = new
{
// 注意:这是 Twilio SendGrid 绑定的示例负载
data = "<h1>Testing Dapr Bindings</h1>This is a test.<br>Bye!",
metadata = new
{
emailTo = "customer@example.com",
subject = "An email from Dapr SendGrid binding",
},
};
await client.InvokeBindingAsync("send-email", "create", email);
- 有关输出绑定的完整指南,请访问操作指南:使用绑定。
检索机密
在检索机密之前,重要的是确保出站通道已注册并准备就绪,否则 SDK 将无法与 Dapr 边车进行双向通信。SDK 提供了一个用于此目的的辅助方法,称为 CheckOutboundHealthAsync。这不是指从 SDK 到运行时的出站,而是指从 Dapr 运行时回到使用 SDK 的客户端应用程序的出站。
此方法只是打开到 https://docs.dapr.io/zh-hans/reference/api/health_api/#wait-for-specific-health-check-against-outbound-path Dapr Health API 中的端点的连接,并评估返回的 HTTP 状态码以确定运行时报告的端点的健康状况。
重要的是要注意,WaitForSidecarAsync 方法和此方法执行几乎相同的操作;WaitForSidecarAsync
无限期轮询 CheckOutboundHealthAsync 端点,直到它返回健康状态值。它们仅用于机密或配置检索等情况。在其他场景中使用它们会导致意外行为(例如,端点永远无法准备就绪,因为没有注册使用"出站"通道的组件)。
此行为将在未来版本中更改,应谨慎依赖。
// 从 DI 获取 DaprClient 实例
var client = scope.GetRequiredService<DaprClient>();
// 等待出站通道建立 - 仅用于此场景,而非一般用途
await client.WaitForOutboundHealthAsync();
// 检索基于键值对的机密 - 返回 Dictionary<string, string>
var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secret");
Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}");
// 从 DI 获取 DaprClient 实例
var client = scope.GetRequiredService<DaprClient>();
// 等待出站通道建立 - 仅用于此场景,而非一般用途
await client.WaitForOutboundHealthAsync();
// 检索基于键值对的机密 - 返回 Dictionary<string, string>
var secrets = await client.GetSecretAsync("mysecretstore", "key-value-pair-secret");
Console.WriteLine($"Got secret keys: {string.Join(", ", secrets.Keys)}");
// 检索单值机密 - 返回 Dictionary<string, string>
// 包含一个以机密名称为键的单个值
var data = await client.GetSecretAsync("mysecretstore", "single-value-secret");
var value = data["single-value-secret"]
Console.WriteLine("Got a secret value, I'm not going to be print it, it's a secret!");
- 有关机密的完整指南,请访问操作指南:检索机密。
获取配置键
// 检索特定键集。
var specificItems = await client.GetConfiguration("configstore", new List<string>() { "key1", "key2" });
Console.WriteLine($"Here are my values:\n{specificItems[0].Key} -> {specificItems[0].Value}\n{specificItems[1].Key} -> {specificItems[1].Value}");
// 通过提供空列表检索所有配置项。
var specificItems = await client.GetConfiguration("configstore", new List<string>());
Console.WriteLine($"I got {configItems.Count} entires!");
foreach (var item in configItems)
{
Console.WriteLine($"{item.Key} -> {item.Value}")
}
订阅配置键
// 订阅配置 API 返回 IAsyncEnumerable<IEnumerable<ConfigurationItem>> 的包装器。
// 通过在 foreach 循环中访问其 Source 来迭代它。当流被中断
// 或取消令牌被取消时,循环将结束。
var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, cts.Token);
await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token))
{
foreach (var item in items)
{
Console.WriteLine($"{item.Key} -> {item.Value}")
}
}
分布式锁(Alpha)
获取锁
using System;
using Dapr.Client;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace LockService
{
class Program
{
[Obsolete("Distributed Lock API is in Alpha, this can be removed once it's stable.")]
static async Task Main(string[] args)
{
const string daprLockName = "lockstore";
const string fileName = "my_file_name";
var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(services =>
{
services.AddDaprClient();
});
var app = builder.Build();
using var scope = app.Services.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<DaprClient>();
// 使用此方法锁定也会自动解锁它,因为这是一个可释放对象
await using (var fileLock = await client.Lock(DAPR_LOCK_NAME, fileName, "random_id_abc123", 60))
{
if (fileLock.Success)
{
Console.WriteLine("Success");
}
else
{
Console.WriteLine($"Failed to lock {fileName}.");
}
}
}
}
}
解锁现有锁
using System;
using Dapr.Client;
namespace LockService
{
class Program
{
static async Task Main(string[] args)
{
var daprLockName = "lockstore";
var builder = Host.CreateDefaultBuilder();
builder.ConfigureServices(services =>
{
services.AddDaprClient();
});
var app = builder.Build();
using var scope = app.Services.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<DaprClient>();
var response = await client.Unlock(DAPR_LOCK_NAME, "my_file_name", "random_id_abc123"));
Console.WriteLine(response.status);
}
}
}
边车 API
边车健康检查
虽然 .NET SDK 提供了一种轮询边车健康状态的方法,但通常不建议开发人员使用此功能,除非他们明确使用 Dapr 来检索机密或配置值。
有两种方法可用:
CheckOutboundHealthAsync查询 Dapr Health API https://docs.dapr.io/zh-hans/reference/api/health_api/#wait-for-specific-health-check-against-outbound-path 中的出站就绪端点,以获取成功的 HTTP 状态码并基于此值报告就绪状态。WaitForSidecarAsync持续轮询CheckOutboundHealthAsync直到它返回成功的状态码。
“出站"方向是指从 Dapr 运行时到你的应用程序的出站通信。如果你的应用程序不使用 Actors、机密管理、配置检索或工作流,运行时将不会尝试创建出站连接。这意味着如果你的应用程序依赖于 WaitForSidecarAsync 而不使用任何这些 Dapr 组件,它将在启动期间无限期锁定,因为永远不会建立端点。
未来的版本将完全删除这些方法,并将其作为内部 SDK 操作执行,因此一般不应依赖任何一种方法。请在 Discord #dotnet-sdk 频道中联系以获取更多说明,了解你的场景是否可能需要使用此功能,但在大多数情况下,不应需要这些方法。
关闭边车
var client = new DaprClientBuilder().Build();
await client.ShutdownSidecarAsync();