Azure SDK for .NET - 专业代码生成与开发指南

24 阅读3分钟

Azure SDK for .NET

Azure SDK for .NET 是一个功能强大的开发工具包,专门用于构建与Azure服务交互的.NET应用程序。该项目提供了完整的代码生成解决方案,能够从TypeSpec或Swagger规范自动生成符合Azure SDK设计指南的高质量客户端库。

功能特性

核心代码生成能力

  • 多规范支持: 支持TypeSpec和Swagger两种API规范格式
  • 自动客户端生成: 从API规范自动生成强类型的客户端代码
  • 协议方法生成: 自动生成基于HTTP协议的底层操作方法
  • 便捷方法生成: 提供类型安全的便捷API层,提升开发体验

开发体验优化

  • 统一设计指南: 所有生成的代码遵循Azure SDK for .NET设计规范
  • 智能序列化: 内置System.Text.Json序列化支持
  • 分布式追踪: 自动集成客户端诊断和追踪功能
  • 异步支持: 完整的异步编程模式支持

工程化特性

  • 测试框架集成: 自动生成测试项目和示例代码
  • 包管理: 完整的NuGet包生成和版本管理
  • AOT兼容性: 支持原生AOT部署的兼容性测试
  • 多目标框架: 支持.NET Standard和多个.NET版本

安装指南

环境要求

  • .NET SDK: 需要.NET 9.0.102 SDK或更高版本
  • Visual Studio: 2022版本(Community或更高),安装.NET桌面开发工作负载
  • Node.js: 22.x.x版本(用于代码生成)
  • PowerShell: 7.0或更高版本
  • Git: 最新版本

项目设置

  1. 克隆仓库并切换到主题分支:
git clone https://github.com/Azure/azure-sdk-for-net.git
cd azure-sdk-for-net
  1. 安装必要的工具和依赖:
dotnet restore
  1. 构建整个仓库:
dotnet build build.proj

构建特定服务

要构建单个服务,使用scope参数:

dotnet build build.proj /p:Scope=servicebus

使用说明

生成新的SDK包

使用TypeSpec生成新的数据平面SDK包:

pwsh eng/scripts/automation/Invoke-TypeSpecDataPlaneGenerateSDKPackage.ps1 `
  -sdkFolder /home/azure-sdk-for-net/sdk/anomalyDetector/Azure.AI.AnomalyDetector `
  -typespecSpecDirectory specification/cognitiveservices/AnomalyDetector `
  -commit ac8e06a2ed0fc1c54663c98f12c8a073f8026b90

生成代码

进入项目src目录并生成代码:

cd sdk/<service>/<package>/src
dotnet build /t:GenerateCode

基本客户端使用

// 使用Azure Key Credential认证
var client = new BasicTypeSpecClient(
    new Uri("https://your-endpoint.azure.com"),
    new AzureKeyCredential("your-api-key"));

// 调用服务方法
var response = await client.SayHiAsync("head-param", "query-param");

核心代码

客户端生成核心逻辑

// AzureClientGenerator.cs - 主要的代码生成器
[Export(typeof(CodeModelGenerator))]
[ExportMetadata(GeneratorMetadataName, nameof(AzureClientGenerator))]
public class AzureClientGenerator : ScmCodeModelGenerator
{
    private static AzureClientGenerator? _instance;
    
    public AzureClientGenerator(GeneratorContext context) : base(context)
    {
        TypeFactory = new AzureTypeFactory();
        _instance = this;
    }

    protected override void Configure()
    {
        base.Configure();
        
        // 包含Azure.Core引用
        AddMetadataReference(typeof(Response).Assembly.Location);
        
        // 添加自定义访问器
        AddVisitor(new ModelFactoryRenamerVisitor());
        AddVisitor(new NamespaceVisitor());
        AddVisitor(new DistributedTracingVisitor());
        AddVisitor(new LroVisitor());
    }
}

类型工厂实现

// AzureTypeFactory.cs - 类型创建工厂
public class AzureTypeFactory : ScmTypeFactory
{
    public override IClientResponseApi ClientResponseApi => 
        AzureClientResponseProvider.Instance;
        
    public override IHttpResponseApi HttpResponseApi => 
        AzureResponseProvider.Instance;
        
    public override IClientPipelineApi ClientPipelineApi => 
        HttpPipelineProvider.Instance;

    protected internal virtual IReadOnlyList<CSharpProjectWriter.CSProjPackage> AzureDependencyPackages =>
    [
        new("Azure.Core", "1.0.0"),
        new("System.Text.Json", "8.0.0")
    ];
}

HTTP管道构建器

// HttpPipelineProvider.cs - HTTP管道配置
public record HttpPipelineProvider : ClientPipelineApi
{
    public override ValueExpression Create(ValueExpression options, ValueExpression perRetryPolicies)
        => Static(typeof(HttpPipelineBuilder))
           .Invoke(nameof(HttpPipelineBuilder.Build), [options, perRetryPolicies]);

    public override MethodBodyStatement[] CreateMessage(
        HttpRequestOptionsApi requestOptions,
        ValueExpression uri,
        ScopedApi<string> method,
        ValueExpression requestContent)
    {
        return
        [
            Declare("message", Original.Invoke("CreateMessage", requestOptions), 
                    out ScopedApi<HttpMessage> message),
            message.Request().SetMethod(method),
            message.Request().SetUri(uri),
            requestContent != Null 
                ? message.Request().Property("Content").Assign(requestContent).Terminate()
                : MethodBodyStatement.Empty,
            ApplyPerCallPolicies(message),
            Return(message)
        ];
    }
}

响应处理系统

// AzureClientResponseProvider.cs - 客户端响应处理
public record AzureClientResponseProvider : ClientResponseApi
{
    public override CSharpType ClientResponseType => typeof(Response);
    public override CSharpType ClientResponseOfTType => typeof(Response<>);
    
    public override ValueExpression CreateAsync(HttpResponseApi response)
        => New.Instance(ClientResponseExceptionType, [response]);

    public override ValueExpression FromValue(ValueExpression valueExpression, HttpResponseApi response)
        => Static(ClientResponseType).Invoke(nameof(FromValue), [valueExpression, response]);
}

分页集合实现

// AzureCollectionResultDefinition.cs - 分页集合处理
public class AzureCollectionResultDefinition : CollectionResultDefinition
{
    protected override string RequestOptionsFieldName => "_context";

    public AzureCollectionResultDefinition(ClientProvider client, 
        InputPagingServiceMethod serviceMethod, 
        CSharpType? itemModelType, 
        bool isAsync) : base(client, serviceMethod, itemModelType, isAsync)
    {
        _operation = serviceMethod.Operation;
        _itemModelType = itemModelType;
        _paging = serviceMethod.Paging;
        _scopeName = client.GetScopeName(_operation);
        _getNextResponseMethodName = $"GetNextResponse{(isAsync ? "Async" : "")}";
        _isProtocol = true;
    }
    
    // 分页迭代逻辑
    public override async IAsyncEnumerable<Page<BinaryData>> AsPages(
        string continuationToken, int? pageSizeHint)
    {
        string nextPage = continuationToken ?? _token;
        while (true)
        {
            Response response = await GetNextResponseAsync(pageSizeHint, nextPage);
            if (response is null) yield break;
            
            var result = (ListWithContinuationTokenResponse)response;
            var items = result.Things.Select(item => BinaryData.FromObjectAsJson(item)).ToList();
            
            yield return Page<BinaryData>.FromValues(items, nextPage, response);
            nextPage = result.NextToken;
            if (nextPage == null) yield break;
        }
    }
}

这些核心组件共同构成了Azure SDK for .NET的强大代码生成能力,确保了生成的客户端库具有一致性、高性能和良好的开发体验。