This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Dapr .NET SDK 最佳实践

高效使用 Dapr .NET SDK

以信心构建

Dapr .NET SDK 提供了丰富的功能集用于构建分布式应用。本节提供在生产场景中高效使用 SDK 的实用指南——聚焦于可靠性、可维护性和开发者体验。

涵盖的主题包括:

  • Dapr 构建块中的错误处理策略
  • 管理实验性功能并抑制相关警告
  • 利用源代码分析器和生成器来减少样板代码并及早发现问题
  • 使用 Dapr.Testcontainers 运行集成测试
  • 基于 Dapr 应用的通用 .NET 开发实践

错误模型指南

Dapr 操作可能因多种原因而失败——网络问题、组件配置错误或瞬态故障。SDK 提供了结构化的错误类型,帮助您区分可重试错误和致命错误。

了解如何有效使用 DaprException 及其派生类型详见此处

实验性属性

部分 SDK 功能被标记为实验性,可能在未来的版本中发生变化。这些功能使用 [Experimental] 进行标注,默认情况下会生成构建时警告。您可以:

  • 使用 #pragma warning disable 选择性抑制警告
  • 使用 SuppressMessage 属性进行更精细的控制
  • 跟踪整个代码库中的实验性功能使用情况

了解更多关于 [Experimental] 属性的使用详见此处

源代码工具

SDK 包含基于 Roslyn 的分析器和源代码生成器,帮助您更轻松地编写更好的代码。这些工具能够:

  • 对 SDK 的常见误用发出警告
  • 为 Actor 注册和调用生成样板代码
  • 支持 IDE 集成以提供更快的反馈

阅读更多关于如何安装和使用这些分析器的内容详见此处

其他指南

本节旨在支持广泛的开发场景。随着应用复杂性的增长,您会发现越来越多与 .NET 中 Dapr 相关的实践和模式——从 Actor 生命周期管理到配置策略和性能调优。

了解如何使用 Dapr.Testcontainers 运行集成测试详见此处

1 - Dapr .NET SDK 中的错误模型

了解如何使用 .NET SDK 中更丰富的错误模型。

Dapr .NET SDK 支持 Dapr 运行时实现的更丰富的错误模型。该模型为应用程序提供了一种使用额外上下文来丰富错误的方式, 使应用程序的使用者能够更好地理解问题并更快地解决它。您可以在这里阅读有关更丰富的错误模型的更多信息,并可以在这里找到实现这些错误的 Dapr proto 文件。

Dapr .NET SDK 实现了 Dapr 运行时支持的所有详细信息,在 Dapr.Common.Exceptions 命名空间中实现,并通过 DaprException 扩展方法 TryGetExtendedErrorInfo 访问。目前,此详细信息提取仅支持存在详细信息的 RpcException

// ExtendedErrorInfo 的使用示例

try
{
    // 使用 Dapr 客户端执行某些会抛出 DaprException 的操作。
}
catch (DaprException daprEx)
{
    if (daprEx.TryGetExtendedErrorInfo(out DaprExtendedErrorInfo errorInfo))
    {
        Console.WriteLine(errorInfo.Code);
        Console.WriteLine(errorInfo.Message);

        foreach (DaprExtendedErrorDetail detail in errorInfo.Details)
        {
            Console.WriteLine(detail.ErrorType);
            switch (detail.ErrorType)
            {
                case ExtendedErrorType.ErrorInfo:
                    Console.WriteLine(detail.Reason);
                    Console.WriteLine(detail.Domain);
                    break;
                default:
                    Console.WriteLine(detail.TypeUrl);
                    break;
            }
        }
    }
}

DaprExtendedErrorInfo

包含与错误关联的 Code(状态代码)和 Message(错误消息),从内部的 RpcException 解析而来。 还包含从异常的详细信息中解析的 DaprExtendedErrorDetails 集合。

DaprExtendedErrorDetail

所有详细信息都实现抽象的 DaprExtendedErrorDetail 并具有关联的 DaprExtendedErrorType

  1. RetryInfo

  2. DebugInfo

  3. QuotaFailure

  4. PreconditionFailure

  5. RequestInfo

  6. LocalizedMessage

  7. BadRequest

  8. ErrorInfo

  9. Help

  10. ResourceInfo

  11. Unknown

RetryInfo

通知客户端在重试之前应等待多长时间的信息。提供一个带有 Second(秒偏移量)和 Nano(纳秒偏移量)属性的 DaprRetryDelay

DebugInfo

服务器提供的调试信息。包含 StackEntries(包含堆栈跟踪的字符串集合)和 Detail(进一步的调试信息)。

QuotaFailure

与可能已达到的配额相关的信息,例如 API 的每日使用限制。它有一个属性 ViolationsDaprQuotaFailureViolation 的集合,每个集合包含一个 Subject(请求的主题)和 Description(有关失败的更多信息)。

PreconditionFailure

通知客户端某些必需的前提条件未满足的信息。有一个属性 ViolationsDaprPreconditionFailureViolation 的集合, 每个集合具有 Subject(前提条件失败发生的主题,例如 “Azure”)、Type(前提条件类型的表示,例如 “TermsOfService”)和 Description(进一步描述,例如 “ToS must be accepted.")。

RequestInfo

服务器返回的信息,服务器可以使用该信息来标识客户端的请求。包含 RequestIdServingData 属性, RequestId 是服务器可以解释的某个字符串(例如 UID),ServingData 是构成请求的一部分的任意数据。

LocalizedMessage

包含本地化消息以及消息的区域设置。包含 Locale(区域设置,例如 “en-US”)和 Message(本地化消息)。

BadRequest

描述错误的请求字段。包含 DaprBadRequestDetailFieldViolation 集合,每个集合具有 Field(请求中的错误字段,例如 ‘first_name’)和 Description(详细说明原因的更多信息,例如 “first_name cannot contain special characters”)。

ErrorInfo

详细说明错误的原因。包含三个属性:Reason(错误的原因,应采用 UPPER_SNAKE_CASE 的形式,例如 DAPR_INVALID_KEY)、 Domain(错误所属的域,例如 ‘dapr.io’)和 Metadata,一个包含更多信息的基于键/值的集合。

Help

为客户端提供资源以对问题进行进一步研究。包含 DaprHelpDetailLink 集合, 该集合提供 Url(指向帮助或文档的 url)和 Description(对链接提供的描述)。

ResourceInfo

提供与访问的资源相关的信息。提供三个属性:ResourceType(正在访问的资源的类型,例如 “Azure service bus”)、 ResourceName(资源的名称,例如 “my-configured-service-bus”)、Owner(资源的所有者,例如 “subscriptionowner@dapr.io”)和 Description(与错误相关的资源的更多信息,例如 “missing permissions to use this resource”)。

Unknown

当详细信息类型 url 无法映射到正确的 DaprExtendedErrorDetail 实现时返回。 提供一个属性 TypeUrl(无法解析的类型 url,例如 “type.googleapis.com/Google.rpc.UnrecognizedType”)。

2 - Experimental Attributes

了解为什么我们用 [Experimental] 属性标记某些方法

Experimental Attributes

Experimental Attributes 简介

随着 .NET 8 的发布,C# 12 引入了 [Experimental] 属性,它提供了一种标准化的方式来标记仍在开发或实验阶段的 API。该属性定义在 System.Diagnostics.CodeAnalysis 命名空间中,需要一个诊断 ID 参数,用于在使用实验性 API 时生成编译器警告。

在 Dapr .NET SDK 中,我们现在使用 [Experimental] 属性而不是 [Obsolete] 来标记尚未通过稳定生命周期认证的构建块和组件。这种方法提供了更清晰的区分:

  1. Experimental APIs — 已经可用但仍在演进的功能,尚未根据 Dapr Component Certification Lifecycle 认证为稳定。

  2. Obsolete APIs — 真正已被弃用并将在未来版本中移除的功能。

在 Dapr .NET SDK 中的使用

在 Dapr .NET SDK 中,我们在类级别为仍处于 Component Certification Lifecycle 的 Alpha 或 Beta 阶段的构建块应用 [Experimental] 属性。该属性包括:

  • 一个用于标识实验性构建块的诊断 ID
  • 一个指向该构建块相关文档的 URL

例如:

using System.Diagnostics.CodeAnalysis;
namespace Dapr.Cryptography.Encryption 
{ 
    [Experimental("DAPR_CRYPTOGRAPHY", UrlFormat = "https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/")] 
    public class DaprEncryptionClient 
    { 
        // Implementation 
    } 
}

诊断 ID 遵循 DAPR_[BUILDING_BLOCK_NAME] 的命名约定,例如:

  • DAPR_CONVERSATION — 用于 Conversation 构建块
  • DAPR_CRYPTOGRAPHY — 用于 Cryptography 构建块
  • DAPR_JOBS — 用于 Jobs 构建块
  • DAPR_DISTRIBUTEDLOCK — 用于 Distributed Lock 构建块

抑制 Experimental 警告

当您使用标记有 [Experimental] 属性的 API 时,编译器会生成错误。要在不将自己的代码标记为实验性的情况下构建解决方案,您需要抑制这些错误。以下是几种方法:

选项 1:使用 #pragma 指令

您可以使用 #pragma warning 指令来抑制特定代码段的警告:

// Disable experimental warning 
#pragma warning disable DAPR_CRYPTOGRAPHY 
// Your code using the experimental API 
var client = new DaprEncryptionClient(); 
// Re-enable the warning 
#pragma warning restore DAPR_CRYPTOGRAPHY

这种方法适用于只想抑制代码中特定部分的警告。

选项 2:项目级抑制

要为整个项目抑制警告,请将以下内容添加到您的 .csproj 文件中。

<PropertyGroup>
    <NoWarn>$(NoWarn);DAPR_CRYPTOGRAPHY</NoWarn>
</PropertyGroup>

您可以包含多个用分号分隔的诊断 ID:

<PropertyGroup>
    <NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
</PropertyGroup>

这种方法对于需要使用实验性 API 的测试项目特别有用。

选项 3:目录级抑制

要为目录中的多个项目抑制警告,请添加一个 Directory.Build.props 文件:

<PropertyGroup>
    <NoWarn>$(NoWarn);DAPR_CONVERSATION;DAPR_JOBS;DAPR_DISTRIBUTEDLOCK;DAPR_CRYPTOGRAPHY</NoWarn>
</PropertyGroup>

该文件应放置在测试项目的根目录中。您可以在 MSBuild 文档 中了解更多关于使用 Directory.Build.props 文件的信息。

Experimental API 的生命周期

随着构建块通过认证生命周期并达到 “Stable” 阶段,[Experimental] 属性将被移除。此时用户无需进行任何迁移或代码更改,只需移除之前添加的警告抑制即可。

相反,[Obsolete] 属性现在将专门保留给真正被弃用并计划移除的 API。当您看到标记有 [Obsolete] 的方法或类时,应根据属性消息中提供的迁移指导计划迁移。

最佳实践

  1. 在应用程序代码中:

    • 谨慎使用实验性 API,因为它们可能会在未来版本中发生变化
    • 考虑将实验性 API 的使用隔离,以便将来更容易更新
    • 记录您对实验性 API 的使用,以便团队了解
  2. 在测试代码中:

    • 使用项目级抑制以避免在测试代码中充斥警告抑制
    • 定期审查您使用的实验性 API,并检查它们是否已稳定
  3. 在为 SDK 做贡献时:

    • 对于尚未完成认证的新构建块使用 [Experimental]
    • 仅对真正被弃用的 API 使用 [Obsolete]
    • UrlFormat 参数中提供清晰的文档链接

其他资源

3 - Integration testing with Dapr.Testcontainers

使用 Dapr.Testcontainers 针对真实基础设施运行 Dapr 集成测试

概述

Dapr.Testcontainers 是一个辅助包,用于使用容器针对真实的 Dapr 运行时组件编写集成测试。它封装了 Testcontainers 库来启动 Dapr 边车、控制平面服务(placement 和 scheduler)以及特定 Dapr 构建块所需的基础设施。

  • Dapr.Testcontainers(核心 harness 和基础设施)
  • Dapr.Testcontainers.Xunit(可选的 xUnit 辅助工具)

前置条件

  • 一个容器运行时(Docker Desktop、Podman 或等效工具)。
  • 能够拉取 Dapr 和依赖镜像的网络访问。

核心概念

Dapr.Testcontainers 围绕环境harness来建模测试:

  • DaprTestEnvironment:共享基础设施(网络、placement、scheduler、可选 Redis)。当你需要多个应用共享 Dapr 控制平面或运行多应用测试时使用它。
  • DaprHarnessBuilder:为特定构建块(工作流、作业、分布式锁或对话)创建 harness。
  • DaprTestApplicationBuilder:使用 harness 启动你的测试应用,并将 Dapr 端点连接到配置。

基本工作流测试示例

下面的示例反映了 .NET SDK 测试套件中的工作流集成测试,展示了典型设置:

var componentsDir = TestDirectoryManager.CreateTestDirectory("workflow-components");

await using var environment = await DaprTestEnvironment.CreateWithPooledNetworkAsync(needsActorState: true);
await environment.StartAsync();

var harness = new DaprHarnessBuilder(componentsDir)
    .WithEnvironment(environment)
    .BuildWorkflow();

await using var testApp = await DaprHarnessBuilder.ForHarness(harness)
    .ConfigureServices(builder =>
    {
        builder.Services.AddDaprWorkflowBuilder(opt =>
        {
            opt.RegisterWorkflow<TestWorkflow>();
        });
    })
    .BuildAndStartAsync();

using var scope = testApp.CreateScope();
var workflowClient = scope.ServiceProvider.GetRequiredService<DaprWorkflowClient>();

await workflowClient.ScheduleNewWorkflowAsync(nameof(TestWorkflow), input: 42);

配置 Dapr 运行时

DaprRuntimeOptions 让你控制 Dapr 镜像版本、App ID、日志级别和容器日志:

var options = new DaprRuntimeOptions("1.17.0")
    .WithAppId("test-app")
    .WithLogLevel(DaprLogLevel.Debug)
    .WithContainerLogs();

var harness = new DaprHarnessBuilder(componentsDir)
    .WithOptions(options)
    .BuildWorkflow();

你也可以通过 DAPR_RUNTIME_VERSION 环境变量全局设置运行时版本。

xUnit 辅助工具

如果你使用 xUnit,Dapr.Testcontainers.Xunit 包包含一个辅助特性,可在运行时版本过低时跳过测试:

[MinimumDaprRuntimeFact("1.17.0")]
public async Task RequiresLatestRuntime()
{
    // ...
}

后续步骤

  • 查看 Dapr .NET SDK 仓库中的集成测试项目(例如 test/Dapr.IntegrationTest.Workflow)。
  • 使用与你所测试的构建块匹配的 harness(工作流、作业、分布式锁、对话)。

4 - Dapr 源代码分析器和生成器

用于常见 Dapr 问题的代码分析器和修复

Dapr 支持日益丰富的可选 Roslyn 分析器和代码修复提供程序,用于检查代码中的代码质量问题。从 v1.16 版本开始,开发者可以在每个标准功能包的基础上,从 NuGet 安装额外的项目,从而在解决方案中启用这些分析器。

规则违规通常标记为 InfoWarning,因此如果分析器发现问题,不一定会中断构建。所有代码分析违规都带有前缀 “DAPR”,并通过该前缀后的数字进行唯一区分。

安装和配置分析器

在 v1.16 Dapr 版本发布后,以下包将可通过 NuGet 获取:

  • Dapr.Actors.Analyzers
  • Dapr.Jobs.Analyzers
  • Dapr.Workflow.Analyzers

在您希望运行分析器的每个项目中安装每个 NuGet 包。该包将作为项目依赖项安装,分析器将在您编写代码时或作为 CI/CD 构建的一部分运行。分析器会标记现有代码中的问题,并在构建项目时警告您新出现的问题。

我们的许多分析器都有关联的代码修复,可以自动纠正问题。如果您的 IDE 支持此功能,任何可用的代码修复都将作为代码中的内联菜单选项显示。

此外,我们的大多数分析器还应该报告代码中被识别为规则关键方面的特定语法所在的行号和列号。如果您的 IDE 支持,双击任何分析器警告应该直接跳转到违反分析器规则的代码部分。

抑制特定分析器

如果您希望阻止分析器对项目的某个特定部分进行检查,可以通过多种方式单独抑制其输出。有关在项目或文件中抑制分析器的更多信息,请阅读相关的 .NET 文档

禁用所有分析器

如果您希望在不移除任何提供分析器的包的情况下禁用项目中的所有分析器,请在 csproj 文件中将 EnableNETAnalyzers 属性设置为 false

可用的分析器

诊断 IDDapr 包类别严重性添加版本描述可用代码修复
DAPR1301Dapr.WorkflowUsageWarning1.16工作流类型未向依赖注入提供程序注册Yes
DAPR1302Dapr.WorkflowUsageWarning1.16工作流活动类型未向依赖注入提供程序注册Yes
DAPR1401Dapr.ActorsUsageWarning1.16Actor 计时器方法调用要求在类型上存在指定的回调方法No
DAPR1402Dapr.ActorsUsageWarning1.16Actor 类型未向依赖注入注册Yes
DAPR1403Dapr.ActorsInteroperabilityInfo1.16将 options.UseJsonSerialization 设置为 true 以支持与非 .NET actor 的互操作性Yes
DAPR1404Dapr.ActorsUsageWarning1.16调用 app.MapActorsHandlers 以映射 Dapr actor 的端点Yes
DAPR1501Dapr.JobsUsageWarning1.16Job 调用要求为 IEndpointRouteBuilder 上每个预期的作业设置和配置 MapDaprScheduledJobHandlerNo

分析器类别

以下是分析器可以分配到的各个有效类别,这些类别是按照 .NET 分析器使用的标准类别建模的:

  • Design
  • Documentation
  • Globalization
  • Interoperability
  • Maintainability
  • Naming
  • Performance
  • Reliability
  • Security
  • Usage