Dapr 的 C# SDK 依赖本地 Dapr Sidecar 进程,DaprClient 仅向 localhost:3500 发送 HTTP/gRPC 请求,未启动 sidecar 必报连接拒绝;开发须用 dapr run 启动应用以注入环境变量,配置推荐默认构造(读环境变量)、指定地址(测试/容器)或 Builder(定制序列化);State 写入失败主因是 statestore 组件未正确加载或配置错误。

直接说结论:Dapr 的 C# SDK 不是“用一下就行”的类库,它依赖 Dapr Sidecar 进程通信,DaprClient 本身不连服务、不处理网络,只发 HTTP/gRPC 请求到本地 localhost:3500(或 gRPC 端口)。没跑起 sidecar,DaprClient 调用必抛 HttpRequestException 或连接拒绝。
为什么 new DaprClient() 后一调用就报错?
常见现象是调用 client.SaveStateAsync("statestore", "key", value) 时抛出 System.Net.Http.HttpRequestException: Connection refused —— 这不是代码写错了,而是 Dapr sidecar 根本没启动。
- 必须先在终端执行
dapr run --app-id myapp --app-port 5000 -- dotnet run(.NET 6+ 项目),否则DaprClient发出的请求无处可去 - 开发时别用
dotnet run单独跑程序,sidecar 和应用必须由dapr run统一托管,才能自动注入环境变量(如DAPR_HTTP_PORT=3500) - 若用 Docker Compose,确保
dapr容器与应用容器在同一 network,并通过服务名访问(如http://dapr:3500),而非localhost
DaprClient 配置方式选哪个?
DaprClient 有三种构造方式,区别不在功能,而在连接目标和调试便利性:
- 默认无参构造:
new DaprClient()→ 自动读DAPR_HTTP_PORT环境变量,开发时最省事,但 CI/CD 中若变量缺失会 fallback 到 3500,容易掩盖配置问题 - 指定地址:
DaprClient.CreateInvokeClient("http://localhost:3500")→ 显式控制端点,适合测试或容器内硬编码(如http://dapr:3500),但绕过环境变量,失去灵活性 - 用 Builder:
DaprClientBuilder().UseJsonSerializerOptions(...).Build()→ 唯一能定制序列化行为的方式,比如要兼容 Java 服务传来的 snake_case 字段,就得配PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
State API 写入失败的三个典型原因
调用 SaveStateAsync 返回成功,但查不到数据?大概率是 statestore 组件没配对或没启用:
- 检查
components/statestore.yaml是否存在且被dapr run加载(加--components-path ./components参数);文件名必须是.yaml,.yml不识别 - 确认
metadata下的redisHost(Redis)或connectionString(Azure Table)真实可达;Dapr 不校验连接,写入时才报ERR unknown command 'JSON.SET'这类底层错误 - State key 默认带前缀(如
myapp||key),若用 Redis CLI 直接GET key查不到——得用GET myapp||key,或者初始化DaprClient时传new StateOptions { KeyPrefix = "" }关掉前缀
Sidecar 是 Dapr 的命门,所有“Dapr 不工作”的问题,80% 出在它没起来、端口被占、组件路径错、或 YAML 语法不合法(YAML 对缩进极其敏感)。别急着改 C# 代码,先 dapr status -k(K8s)或看 dapr run 启动日志里有没有 time="..." level=info msg="component loaded"。


















