ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Aspire 多语言集成导出(Polyglot Exports)编写指南:让 C Hosting 集成面向 TypeScript 等语言 AppHost 生成类型化 SDK

Aspire 多语言集成导出(Polyglot Exports)编写指南:让 C Hosting 集成面向 TypeScript 等语言 AppHost 生成类型化 SDK Aspire 多语言集成导出Polyglot Exports编写指南让 C# Hosting 集成面向 TypeScript 等语言 AppHost 生成类型化 SDK【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspireAspire 的 Hosting 集成是 C# 库但多语言 AppHostTypeScript、Python、Go、Java、Rust通过 Aspire CLI 生成的 SDK 来调用它们。本文基于仓库中 hosting-integration-authoring 技能包下的 polyglot-exports.md 展开完整覆盖 ATS 契约设计、[AspireExport]系列属性、DTO/Union/回调上下文/Value 目录、分析器诊断ASPIREEXPORT001–016以及本地生成 SDK 的验证方法并结合仓库源码AspireExportAttribute、集成分析器包说明、第三方属性复制指南佐证其底层机制。读完本文你可以按照 Aspire 仓库的官方规范编写一个可被多语言 AppHost 正确消费、通过分析器零告警构建的 ATS 导出集成。一、工作机制CLI 扫描 ATS 元数据并生成类型化 SDK多语言 AppHost 不会直接调用 C# 代码。整个链路如下Aspire CLI 加载你的集成程序集扫描程序集中的 ATSAspire Type System元数据如[AspireExport]、[AspireDto]、[AspireUnion]等特性生成目标语言的类型化 SDKTypeScript 下为 promise-heavy 的 API含.d.ts签名与 JSDoc多语言 AppHost 的 SDK 调用通过 JSON-RPC 分派回 C# 侧执行。因此编写多语言兼容集成的第一步不是写 C# API而是先设计 ATS 契约生成的 SDK 用户应该看到哪些方法、DTO、回调、值和文档。C# API 本身保持符合人体工程学然后适配 ATS——显式导出、DTO/options 类型、union、小型 context/editor 类型、忽略 C# 专属重载、内部导出适配器。命名规则保证 C# 与生成 API 对齐参见技能包内的 api-naming-and-shape.md整体技能入口为 SKILL.md更完整的多语言 AppHost 设计背景见 polyglot-apphost.md 规格文档。二、启用集成分析器Analyzer Enablement每一个导出 ATS API 的集成都应有集成分析器覆盖。支持两条路径选其一若项目已经引用了Aspire.Hosting在项目文件中设置EnableAspireIntegrationAnalyzerstrue/EnableAspireIntegrationAnalyzers。仓库中 Aspire.Hosting.targets 会在该属性为true时把分析器注入构建默认值为false即仅引用Aspire.Hosting不会自动启用仓库内的Aspire.Hosting.Azure.Kubernetes、Aspire.Hosting.Azure.Sandboxes等项目文件就是这样启用的。否则直接引用Aspire.Hosting.Integration.Analyzers包使用PrivateAssetsall并保持与 Aspire 包相同的版本ItemGroup PackageReference IncludeAspire.Hosting.Integration.Analyzers VersionAspire 包版本 PrivateAssetsall / /ItemGroup引用即生效无需额外 MSBuild 属性见 ThirdPartyAtsAttributes.md。两个重要的补充事实来自 分析器包 README运行这些分析器的集成默认被视为多语言兼容polyglot-compatible会自动获得polyglotNuGet 标签使aspire add能把集成展示给非 C# AppHost。如果你的项目确实没有[AspireExport]表面例如纯基础设施库必须显式设置IsAspirePolyglotCompatiblefalse/IsAspirePolyglotCompatible来豁免否则没有导出覆盖又不豁免的项目会以ASPIREEXPORT017构建失败提示你补充导出覆盖或显式退出。零告警的干净构建只是基线不够。发布前必须人工检查生成的 SDK 签名和文档。此外常规集成编写中不要手动编辑生成的 API/ATS 基线文件。在 Aspire 仓库中签入的src/Aspire.Hosting*/api/*.cs和*.ats.txt是发布兼容基线PR 验证会独立生成当前表面做比对签入基线的更新由发布/评审流程在 API 变更被接受后统一完成。三、导出属性Export AttributesDO 清单用[AspireExport]标记可被生成 SDK 使用的 API用[AspireExport]标记资源类型使生成的 SDK 能引用类型化句柄只在每个公开属性都应被投影的小型资源/句柄类型上使用[AspireExport(ExposeProperties true)]回调上下文和 editor 属性优先逐个加[AspireExport]而不是整体开启属性投影Aspire 资源名参数加[ResourceName]C# 专属重载、便捷重载、已弃用 API、不支持的类型、实现细节用[AspireExportIgnore(Reason ...)]标注导出方法内联调用同步回调时包括 async 方法在首个await前就调用回调的情况使用[AspireExport(RunSyncOnBackgroundThread true)]接受有限集合活动 AppHost 值形状的方法参数用[AspireUnion(...)]JSON 形状的配置/选项对象用[AspireDto]不可变的预定义值目录模型名、SKU、区域等用[AspireValue]。DONT 清单不要导出每一个 C# 重载指望生成出来的名字天然可用不要依赖 C# 的重载解析——ATS 分派不是 C# 重载解析DTO 中不要暴露插值字符串处理器、日志器、服务提供器、委托、配置对象、可变框架类型或不透明实现句柄不要把EndpointReference、ReferenceExpression、资源、IResourceBuilderT等活动句柄藏进 DTO 里——应作为 union 形状的方法参数或 editor 方法参数接收不要通过宽泛的ExposeProperties暴露仅供 provisioning 使用的句柄如BicepOutputReference——应标记忽略、设为非公开或导出 ATS 兼容投影不要对大型框架风格类型开启宽泛的ExposeProperties或ExposeMethods[AspireExportIgnore]不能省略Reason。源码层面AspireExportAttribute 的 XML 文档明确了 ID 推导约定方法导出的 capability ID 为{AssemblyName}/{camelCaseMethodName}如AddRedis在Aspire.Hosting.Redis中生成Aspire.Hosting.Redis/addRedis仅在需要消歧多个重载时才显式指定id类型导出的 type ID 为{AssemblyName}/{TypeName}。ExposeProperties开启后属性能力命名为{Package}/{TypeName}.{propertyName}getter与{Package}/{TypeName}.set{PropertyName}可写属性。RunSyncOnBackgroundThread的语义是让 ATS 调度器在后台线程上执行该导出使 JSON-RPC 请求循环在方法等待回调完成时仍能处理嵌套的回调与能力调用——这正是内联调用同步回调场景避免死锁的关键。四、XML 文档与 ATS 文档覆盖生成的 SDK 文档来源于 C# 的 XML 文档注释文档本身就是 ATS 契约的一部分。要求为每个导出的方法、DTO、参数、属性、回调上下文、editor 方法、值目录编写语言中立的 XML 注释避免 C# 专属的实现细节描述——生成 SDK 的用户看不到那些类型。当标准 C# XML 文档不能良好翻译成多语言 SDK 时使用 ATS 覆盖标签ATS 标签覆盖对象ats-summarysummaryats-param name...指定的paramats-returnsreturnsats-remarksremarks空的ats-*标签会有意地在生成的 SDK 中抑制对应的标准文档。生成 SDK 内部链接使用ats-see cref!:kind:identifier.path / ats-seealso cref!:kind:identifier.path /支持的kind取值为type、method、field。!:前缀的作用是阻止 C# 编译器校验这个自定义 cref。五、Capability ID 与生成方法名Capability ID 是运行时分派标识符不包含C# 接收者类型、参数列表、泛型约束或重载签名。新增或评审一个导出前回答两个问题生成的 AppHost 调用应该长什么样如果调用方从未见过 C# 的重载集合这个生成的调用是否仍然清晰DO保证 capability ID 在程序集内唯一且稳定约定推导的 ID 已经正确时避免显式指定导出 ID仅当运行时 capability ID 必须唯一、而生成的方法位于不同目标类型、可以安全共享同一个友好方法名时才用MethodName按目标类型逐个检查生成的成员名而不只是看运行时 capability ID对容器注册表等共享概念优先复用已有框架导出——目标专属的便捷包装器可能在不增加能力的情况下造成生成成员名冲突。DONT不要在不同方法间复用显式导出 ID不要设置与约定推导名重复的显式导出 ID会触发 ASPIREEXPORT011不要用MethodName让同一生成目标类型上的多个导出获得相同生成方法名不要假设不同的 C# 接收者类型就能避免 capability 冲突不要仅仅为了改变注解修改行为而导出一个目标专属重载——泛型导出助手已表达相同用户概念时就不必。核心原则一个用户概念 → 一个 ATS 方法变化维度用 DTO/options 对象、[AspireUnion]或内部分派器建模同一生成目标类型上的不同概念用不同的生成方法名。C# 形状与 ATS 形状不一致时内部导出适配器保持 C# API 公开且符合人体工程学另加一个内部导出适配器internal 显式 ID[AspireExport(publishAsStaticWebsite)] internal static IResourceBuilderTResource PublishAsStaticWebsitePolyglotTResource( this IResourceBuilderTResource builder, string? apiPath null, IResourceBuilderIResourceBuilderIResourceWithServiceDiscovery? apiTarget null) where TResource : JavaScriptAppResource { return PublishAsStaticWebsiteCore(builder, apiPath, apiTarget); }常见需要C# API 多语言适配器双轨的场景回调配置ActionT、FuncIResourceBuilderT, IResourceBuilderT或工具专属的选项委托C# 泛型元数据标记IProjectMetadata、包元数据、强类型项目引用无法干净投影的可变字典或框架类型endpoint-reference 重载——生成 SDK 需要 string/parameter/external-service 的替代入参形式。配套规则C# 专属重载用[AspireExportIgnore(Reason ...)]标注reason 中说明不兼容的类型与替代 API多语言友好的重载接收原始类型、DTO、资源构建器、参数或外部服务当生成名应与 C# 概念一致而内部适配器方法需要唯一 CLR 名时使用MethodName。绝对不要把 C# 回调或泛型元数据重载留作配置某个导出功能的唯一途径。六、DTO、Options、Union 与活动值扁平 DTO/options 对象可选配置使用扁平的[AspireDto]对象生成的 SDK 应像普通对象字面量[AspireDto] public sealed class AddMyWorkerOptions { public string? ImageTag { get; init; } public string[] Args { get; init; } []; }TypeScript 侧调用形态const worker await builder.addMyWorker(worker, { imageTag: v1, args: [--debug] });DTO 规则DTO 标记[AspireDto]属性定义见 AspireDtoAttribute保持 JSON 可序列化输入属性使用initsetter使用数组、record、原始类型、枚举、其他 DTO以及T为 ATS 兼容类型的Dictionarystring, T单个可选 options 参数优先扁平 options 对象避免{ options: { ... } }嵌套形状导出的 DTO/model 类型上避免 getter-only 的原始ListT或DictionaryTKey, TValue属性会触发 ASPIREEXPORT016——JSON 输入/输出形状用init可设的数组/DTO 属性活动修改走显式 editor 方法或包装类型。活动 AppHost 值用 Union需要接受活值引用表达式、端点、参数资源等的方法参数用[AspireUnion]显式列出有界类型集合。仓库核心导出中的WithSetting是标准范式[AspireExport] public static IResourceBuilderT WithSettingT( this IResourceBuilderT builder, string name, [AspireUnion( typeof(string), typeof(ReferenceExpression), typeof(EndpointReference), typeof(IResourceBuilderParameterResource), typeof(IResourceBuilderIResourceWithConnectionString), typeof(IExpressionValue))] object value) where T : IResourceWithEnvironment { return WithSettingCore(builder, name, value); }注意[AspireUnion]至少需要两个类型ASPIREEXPORT005且每个成员类型都必须 ATS 兼容ASPIREEXPORT006属性定义见 AspireUnionAttribute。七、回调上下文与 Editor 类型生成的 SDK 回调可以回拨 ATS/RPC——在 TypeScript 中通常意味着回调里可以await生成 SDK 的调用。编写规则DO回调上下文类型保持小巧标记[AspireExport]只导出回调编写者需要的成员可变状态暴露 editor 对象而非原始可变集合editor 使用set、add、remove这类方法必须从生成 SDK 回传的回调修改使用导出的 editor/context 类型内联调用同步回调的导出方法设置RunSyncOnBackgroundThread true机制见第三节的源码说明回调需要服务时通过生成的 service-provider 方法显式获取。DONT除非已知 ATS 兼容且有意属于生成契约不要直接暴露原始Dictionary、List、IServiceProvider或框架上下文对象不要导出修改 DTO/options 对象并期望修改从多语言调用方回传的回调——DTO 建模 JSON 形状的输入不是活动的 editor 状态不要在 RPC 线程上阻塞等待一个可能重入生成 SDK 的回调。示例的 context/editor 形状封装一个原始字典只暴露Set方法[AspireExport] internal sealed class EnvironmentEditor(Dictionarystring, object environmentVariables) { [AspireExport] public void Set( string name, [AspireUnion( typeof(string), typeof(ReferenceExpression), typeof(EndpointReference), typeof(IResourceBuilderParameterResource), typeof(IResourceBuilderIResourceWithConnectionString))] object value) { environmentVariables[name] value; } }八、Exposed Properties / Methods 与生成形状映射ExposeProperties true和ExposeMethods true是宽泛展开开关它们导出类型上每一个兼容的公共成员含适用时的继承成员只应用于小的、专用目的的句柄/上下文类型否则逐成员导出。C# 属性形状到生成的 TypeScript 形状的映射表C# 形状生成形状getter-only 属性async 方法如resource(): PromiseTgetter-only 的AspireListT或AspireDictK,V返回该包装器的 async 方法可设置的可变集合包装器只读同步包装器属性标量读写属性只读PropertyAccessorT带 asyncget()与set(value)不该暴露的属性用[AspireExportIgnore]标注定义见 AspireExportIgnoreAttributeTypeScript 调用方需要修改状态时优先显式方法或 editor 类型而不是暴露宽泛的可变属性。九、Value Catalogs[AspireValue][AspireValue]用于把不可变的预定义值生成到 SDK 中作为类型化常量定义见 AspireValueAttribute应用于公开静态字段或带公开静态 getter 的公开静态属性使用合法的生成 SDK 目录名和标识符路径值在扫描时一次性取值snapped作为生成 SDK 常量输出——不要期望运行期刷新支持复制的形状包括原始类型、枚举、数组、只读字典以及只含上述支持形状的 DTO。不要在值目录中使用ListT、可变DictionaryK,V、运行时句柄、资源、构建器、委托或运行期状态。十、ATS 兼容类型类别导出方法签名中可以使用原始类型string、bool、数值类型值类型DateTime、TimeSpan、Guid、Uri枚举句柄IDistributedApplicationBuilder、IResourceBuilderT、标记了[AspireExport]的资源类型标记[AspireDto]的 DTO标记[AspireValue]的静态值元素类型 ATS 兼容的集合ActionT、FuncT等委托核心导出服务ILogger、IServiceProvider、IConfiguration特殊值类型ParameterResource、ReferenceExpression、EndpointReference、IExpressionValue、CancellationToken兼容类型的可空形式。不兼容的类型包括插值字符串处理器以及没有[AspireExport]或[AspireDto]的自定义复杂类型。十一、分析器诊断对照表ID含义ASPIREEXPORT001独立的[AspireExport]方法必须是 staticASPIREEXPORT002无效的导出 ID 格式ASPIREEXPORT003返回类型不是 ATS 兼容ASPIREEXPORT004参数类型不是 ATS 兼容ASPIREEXPORT005[AspireUnion]至少需要两个类型ASPIREEXPORT006union 中的类型不是 ATS 兼容ASPIREEXPORT007同一目标类型上导出 ID 重复ASPIREEXPORT008导出类型上的公共扩展方法缺少[AspireExport]或[AspireExportIgnore]ASPIREEXPORT009导出名可能与其他集成冲突ASPIREEXPORT010内联调用的同步回调可能死锁ASPIREEXPORT011显式导出 ID 与约定推导名相同ASPIREEXPORT012回调上下文类型缺少[AspireExport]ASPIREEXPORT013同一程序集内多语言 capability ID 跨导出重复ASPIREEXPORT014同一 SDK 目标类型上生成成员名重复ASPIREEXPORT015[AspireExport(Description ...)]已弃用改用 XML 文档ASPIREEXPORT016DTO 属性是 get-only 可变集合添加 init 访问器诊断定义在 AspireExportAnalyzer.Diagnostics.cs主分析逻辑在 AspireExportAnalyzer.cs。另注意如分析器 README所述启用分析器但完全没有[AspireExport]表面、又没有设置IsAspirePolyglotCompatiblefalse的项目会以ASPIREEXPORT017构建失败。一个典型的分析器反馈场景导出的 builder 方法直接调用同步回调委托如ActionIResourceBuilderContainerResource configure立即configure(resource)即会被诊断指出需要RunSyncOnBackgroundThread true或改造签名。十二、本地生成 SDK 验证最终验证用 TypeScript AppHost 直接引用你的集成项目。在aspire.config.json中映射本地 csproj{ appHost: { path: apphost.mts, language: typescript/nodejs }, packages: { MyCompany.Hosting.MyDatabase: ../src/MyCompany.Hosting.MyDatabase/MyCompany.Hosting.MyDatabase.csproj } }然后运行aspire restore或aspire run检查.aspire/modules/下的生成产物逐项验证生成的 import.d.ts方法签名DTO 形状回调上下文访问器属性访问器形状由 XML 与ats-*文档生成的 JSDoccapability/成员名冲突。新的 TypeScript AppHost 使用apphost.mts与.aspire/modules/*.mjs导入生成的 TypeScript API 以 promise 为中心awaitcreateBuilder、fluent 调用、getter-only 属性方法、属性访问器的get()/set(value)调用。十三、补充第三方集成如何复制 ATS 属性如果你的集成不想引用Aspire.Hosting可以自行复制 ATS 属性定义但要严格遵守 ThirdPartyAtsAttributes.md 的约束扫描器按完整类型名发现属性不按具体类型引用匹配复制的属性必须位于Aspire.Hosting命名空间且类型名一致Aspire.Hosting.AspireExportAttribute、Aspire.Hosting.AspireExportIgnoreAttribute、Aspire.Hosting.AspireDtoAttribute、Aspire.Hosting.AspireUnionAttribute构造器签名必须按参数个数与参数类型匹配AspireExportAttribute为()、(string)、(Type)AspireUnionAttribute为(params Type[])属性名必须精确一致Type、Description、MethodName、ExposeProperties、ExposeMethods、Reason、DtoTypeId、Types扫描器使用CustomAttributeData读取元数据而不实例化属性因此你的属性类型无需在扫描时可加载——只有完整名称与支持成员需要匹配若日后同时引用了Aspire.Hosting且自定义属性与官方属性应用于同一成员两者都会被检测到扫描器取第一个匹配。跨程序集的 assembly-level 类型导出行为见 polyglot-apphost.md 规格中的 Cross-Assembly Type Exports 一节。小结Aspire 多语言集成导出的编写可以归纳为一条纪律链先设计 ATS 契约方法/DTO/回调/值/文档再写 C# 实现启用分析器并以零告警构建 人工审查生成 SDK 为发布门槛一个用户概念对应一个 ATS 方法变化用 DTO/union/editor 建模C# 专属形状用[AspireExportIgnore]必须带 Reason与 internal 适配器隔离最后用aspire.config.json指向本地 csproj 的 TypeScript AppHost 做端到端生成验证。配合仓库内 hosting-integration-authoring 技能包 的其他资源文件app model、endpoint、连接属性、run/publish/deploy 模式等即可产出与 Aspire 仓库自身集成同等质量的多语言兼容扩展。【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表