Orleans 序列化代码生成自定义:为 Grain 调用定义自定义返回类型
后端微服务【免费下载链接】orleansCloud Native application framework for .NET项目地址https://gitcode.com/gh_mirrors/or/orleans点击查看免费下载Orleans 的源生成器source generator会为每个 Grain 方法自动生成代理proxy与请求request类型构成 RPC 调用面。对于大多数应用而言Task、ValueTask、IAsyncEnumerableT已足够但当库需要提供明确完成、取消、失败、生命周期、分配与并发语义的调用抽象时可以通过自定义返回类型 自定义 invokable 请求基类来扩展这套生成面。本文以仓库中的完整示例 CustomGrainCallReturnType 为主线讲解InvokableBaseTypeAttribute、ReturnValueProxyAttribute、GeneratedActivatorConstructorAttribute等扩展点的注册位置、解析优先级、验证要求与跨程序集发布策略并结合源码说明底层工作原理。扩展点概述什么时候需要自定义返回类型Orleans 会为实现 Grain 调用的代理与请求类型生成代码。高级库可以将一个返回类型与一个自定义 invokable 请求基类关联从而扩展生成的 RPC 表面返回类型定义了面向应用方的调用模型请求基类则决定了生成的请求如何进入 Orleans 运行时、目标结果如何变成响应。适用场景是库需要一种具备显式语义的调用抽象而这种语义超出Task、ValueTask或IAsyncEnumerableT所能表达的范围。此时库必须自己拥有该抽象的完成completion、取消cancellation、失败failure、生命周期lifetime、分配allocation与并发concurrency契约。例如示例中的GrainCallT是基于任务task-backed的返回类型调用生成的代理会立即发起一次 Orleans 请求返回的值可被多次 await、缓存终止结果并通过底层任务传播远端失败。完整可运行应用见 samples 目录中的 CustomGrainCallReturnType 条目其核心实现位于 samples/CustomGrainCallReturnType/。定义一个可等待的返回类型GrainCallT是面向应用的返回类型。下面这段代码来自文档 snippet CustomGrainCallReturnType.cs与仓库示例 GrainCall.cs 一一对应[InvokableBaseType( typeof(GrainReference), typeof(GrainCall), typeof(GrainCallRequest))] public sealed class GrainCallT { private readonly TaskT? _task; private GrainCall(TaskT? task) _task task; public bool IsCompleted _task.IsCompleted; public TaskT? AsTask() _task; public TaskAwaiterT? GetAwaiter() _task.GetAwaiter(); public static GrainCallT FromResult(T? value) new(Task.FromResult(value)); internal static GrainCallT FromInvocation(ValueTaskT? invocation) new(invocation.AsTask()); }要点说明类型本身是一个**可等待awaitable**的包装器通过公开GetAwaiter()返回TaskAwaiterT和AsTask()应用方可以直接await调用结果也可以把它转成Task使用。InvokableBaseTypeAttribute注册的是开放泛型族它把GrainCall与GrainCallRequest关联到代理基类GrainReference上。对每一个返回GrainCallT的方法生成器都会用相同的T关闭出GrainCallRequestT。FromResult与FromInvocation是包装器的两个构造入口前者用于 Grain 实现方直接返回结果后者用于代理侧把IGrainReferenceRuntime.InvokeMethodAsync返回的ValueTask转为可缓存的任务。该类型不参与序列化——它是纯应用面包装器真正序列化的是生成出的请求类型。适配生成的请求请求基类的两个职责请求基类GrainCallRequestT完成两项工作调用方一侧其初始化器把生成的请求通过IGrainReferenceRuntime提交并返回面向应用的GrainCallT。目标一侧其IInvokable.Invoke()实现 await Grain 实现返回的值并创建Response。[SerializerTransparent] [ReturnValueProxy(nameof(InitializeRequest))] public abstract class GrainCallRequestT : RequestBase { [NonSerialized] private readonly IGrainReferenceRuntime _runtime; [GeneratedActivatorConstructor] protected GrainCallRequest(IGrainReferenceRuntime runtime) _runtime runtime; public GrainCallT InitializeRequest(GrainReference proxy) GrainCallT.FromInvocation( _runtime.InvokeMethodAsyncT(proxy, this, Options)); public sealed override ValueTaskResponse Invoke() { try { return CompleteAsync(InvokeInner()); } catch (Exception exception) { return ValueTask.FromResult(Response.FromException(exception)); } } private static async ValueTaskResponse CompleteAsync(GrainCallT call) { try { return Response.FromResult(await call); } catch (Exception exception) { return Response.FromException(exception); } } protected abstract GrainCallT InvokeInner(); }三个关键特性的作用如下ReturnValueProxyAttribute告诉生成的代理直接返回request.InitializeRequest(this)的返回值而不是把请求交给运行时去做传统的请求-响应式调用。也就是说初始化器是“启动操作 / 创建应用面适配器”的交接点handoff point。源码中生成器在 InvokableGenerator.cs 里读取该特性的构造参数初始化器方法名并据此改变代理方法的生成方式。Orleans 会校验重载解析选择的是一个可访问的、具体的、非泛型的实例方法该方法接收一个按值传入的生成的代理参数且返回值能隐式转换为 Grain 方法声明的返回类型。GeneratedActivatorConstructorAttribute用于为生成的请求激活选择依赖注入构造器。它继承自ActivatorUtilitiesConstructorAttribute指示生成的 activator 实现使用该构造器来激活请求实例。当请求基类不需要任何服务时无参构造器同样可行包括带可选参数或params的构造器。[NonSerialized] 字段标记运行时专用字段如示例中的_runtime这些字段不会进入序列化的请求载荷生成出的参数字段才是序列化载荷本身。RequestBase基类位于 src/Orleans.Core.Abstractions/Runtime/GrainReference.cs被标记为[SerializerTransparent]提供InvokeMethodOptions Options、参数读写、Invoke()、目标获取/设置、Dispose()等抽象成员。示例中的_runtime.InvokeMethodAsyncT对应接口 IGrainReferenceRuntime 上的ValueTaskT? InvokeMethodAsyncT(GrainReference reference, IInvokable request, InvokeMethodOptions options)。在 Grain 契约中使用自定义返回类型接口与实现两侧都使用自定义返回类型。生成的请求会用相同签名重写InvokeInner而请求基类决定其值如何被完成与传输public interface ICalculatorGrain : IGrainWithStringKey { GrainCallint Add(int left, int right); GrainCallint Fail(string message); } public sealed class CalculatorGrain : Grain, ICalculatorGrain { public GrainCallint Add(int left, int right) GrainCallint.FromResult(left right); public GrainCallint Fail(string message) throw new InvalidOperationException(message); }完整契约见 CalculatorGrain.cs客户端调用方式见 Program.cs先await calculator.Add(20, 22)打印20 22 42再await calculator.Fail(...)捕获从远端传播回来的InvalidOperationException。该样本契约的语义可以归纳为五个维度文档原文即以此方式定义契约这也正是“库拥有抽象契约”的含义完成Completion代理初始化器立即提交一次请求任务在 Orleans 响应到达时完成。失败Failure同步的 Grain 失败、以及 awaitGrainCallT期间产生的失败都会变成 Orleans 的异常响应并重新抛给调用方。在请求基类中同步异常由Invoke()的 catch 捕获并转为Response.FromException异步失败由CompleteAsync的 catch 捕获并转为Response.FromException。取消CancellationCancellationToken作为 Grain 方法参数参与 Orleans 的协作式调用取消。示例包装器本身不添加独立的取消来源。生命周期Lifetime请求提交后由运行时拥有包装器只保留代表该次调用的任务。并发Concurrency基于任务的包装器支持多个 awaiter 观察同一个终止结果它代表一次调用绝不重新提交。自定义适配器可以实现其他策略例如延迟提交lazy submission、流式streaming或订阅subscriptions。文档明确提示这些策略应作为公开返回类型契约的一部分加以说明并且要考虑到 Grain 激活的生命周期、释放disposal、背压backpressure以及被遗弃的消费者abandoned consumers。注册位置与解析优先级InvokableBaseTypeAttribute构造函数签名(proxyBaseClass, returnType, invokableBaseType)见 Annotations.cs可以出现在四个位置注册位置用途应用到 Grain 方法上的特性类型attribute type为携带该特性的方法选择行为。返回类型定义该返回类型库自有的默认适配器。程序集连接由独立库拥有的返回类型与代理基类。代理基类上的DefaultInvokableBaseTypeAttribute定义代理内建built-in的返回族。解析过程分**两遍two passes**进行Orleans 先考虑精确构造返回类型exact constructed return type的匹配再考虑开放泛型匹配open-generic matching。在每一遍内部优先级为方法特性 返回类型 程序集注册 代理默认值。因此一条精确的程序集注册会优先于一条开放泛型的方法注册。其他几条关键规则每条注册都限定在代理基类的原始泛型定义original generic definition范围内。例如GrainReference上的映射不会影响另一个代理层级程序集注册可以添加映射包括引用适配器程序集提供的映射但不能替换代理内建默认映射。完全相同的注册会合并coalesce。若在同一胜出位置上存在不同的 invokable 基类会产生**确定性deterministic**的构建诊断按类型与程序集标识排序——这样引用的顺序不会改变生成的代码行为。相关诊断 ID 为ORLEANS0111InvalidInvokableBaseTypeMapping定义见 DiagnosticRuleId.cs。在生成器内部代理基类的映射集合会被复制为方法级字典的默认值随后方法上的特性属性按InvokableBaseTypeAttribute合并进来见 InvokableMethodDescription.cs这正是“默认值→方法特性覆盖”优先级的实现来源。精确映射与开放泛型映射精确映射把一个已构造的返回类型例如GrainCallint与一个请求基类关联它会覆盖GrainCall的开放映射。开放泛型返回映射要求请求基类同为开放泛型且元数arity相同Orleans 会用返回类型的类型实参关闭请求基类并校验每一个泛型约束。因此一个已关闭closed的请求基类无法服务开放返回族。实践建议用精确映射应对专门的协议或优化把开放映射保留为族级契约这样新增的构造返回类型会获得一致的行为。生成器的验证要求在消费编译consuming compilation中生成器会对被选中的请求基类做如下校验它是可访问的、非 static、非 sealed 的类。它的泛型元数与开放返回类型匹配且构造类型实参满足其约束。生成的派生请求可以调用一个可访问的无参构造器包括无歧义的可选参数或params构造器或一个标记了GeneratedActivatorConstructorAttribute的可访问构造器。依赖注入构造器的参数均为按值传递并能通过生成的派生构造器无歧义地绑定。标记了ReturnValueProxyAttribute的初始化器从生成的代理类型绑定并返回声明的自定义返回类型。这些诊断应被当作**扩展契约失败extension-contract failures**处理它们会指出注册位置便于库修正其已发布的映射。跨程序集库适配器程序集的注册方式当返回类型与代理基类由独立库拥有时适配器包可以注册来自这些独立程序集的类型。文档 snippet 展示了程序集级注册的写法注意是[assembly: ...][assembly: InvokableBaseType( typeof(GrainReference), typeof(Serialization.GrainCall), typeof(Serialization.GrainCallRequest))]使用约束与发布建议消费项目必须同时引用返回类型所有者、代理所有者以及适配器程序集。生成器会从源码与引用程序集读取程序集特性然后在消费编译中校验可访问性与绑定。公开类型与成员给每个消费者相同的映射而internal成员要求与每个生成的代理程序集建立显式的友元程序集friend-assembly关系。发布时应把返回类型、请求基类、注册和所需的序列化元数据作为一个带版本号的兼容单元versioned compatibility unit并验证在“不同程序集中生成代理”的消费者以及“调用方与 silo 运行相邻包版本”的部署场景。标识、序列化与兼容性生成的请求类型就是序列化的消息体。Orleans 会为它赋予一个复合标识compound identity包含调用标记、代理标识、Grain 接口类型与方法标识。方法标识来自 IdAttribute、AliasAttribute或确定性的签名哈希显式别名能在 CLR 名称改变时保持标识稳定。值得注意的兼容性要点自定义请求基类控制调用行为而生成成员持有方法参数与目标分发元数据。保持序列化成员 ID 与别名稳定并在滚动升级rolling upgrade期间保持参数与结果类型的兼容。更改映射可能改变生成请求的基类行为——即使 Grain 方法签名未变跨调用方与 silo 版本的请求也可能不同。所有生成的请求参数与响应值都由同一套 Orleans.Serialization 编解码器、copier、activator 与 converter 处理。发布适配器库之前建议通读文档 序列化与代码生成内部实现 了解底层机制。运行示例仓库提供了可直接运行的控制台示例samples/CustomGrainCallReturnType/它基于 .NET 10使用 localhost 集群UseLocalhostClustering()无需任何外部服务dotnet run --project CustomGrainCallReturnType.csproj运行后客户端会先打印成功结果20 22 42随后观察到 Grain 返回的InvalidOperationException消息为The grain reported a sample failure.完整流程见 Program.cs。其他相关自定义面围绕序列化与代码生成Orleans 文档体系还提供了多个相邻的扩展面可作为本篇的延伸阅读自定义序列化IGeneralizedCodec、IGeneralizedCopier与类型过滤器。配置序列化通过ISerializerBuilder注册 codec、copier、activator、converter 或外部序列化器。声明不可变类型ImmutableAttribute。生成序列化器与稳定标识GenerateSerializerAttribute、IdAttribute、AliasAttribute。检查或修改生成的请求参数在 Grain 调用过滤器grain call filters中处理。用生成的请求元数据进行调度。结合示例 CustomGrainCallReturnType 与源码 Orleans.CodeGenerator 阅读可以完整看到“应用面返回类型 → 生成请求 → 运行时分发 → 响应回传”的整条调用链并据此在自己的库中设计、注册与发布自定义的 Grain 调用返回类型。赞分享后端微服务【免费下载链接】orleansCloud Native application framework for .NET项目地址https://gitcode.com/gh_mirrors/or/orleans点击查看免费下载相关推荐BDI 心智状态 RDF/Turtle 建模实战Agent Skills for Context Engineering 完整认知工作流示例BDI 心智状态 RDF/Turtle 建模实战Agent Skills for Context Engineering 完整认知工作流示例 本文是 Agen后端微服务Orleans 诊断 ORLEANS0026 完全指南自定义 Grain 调用返回类型的 InvokableBaseType 映射失效与修复Orleans 诊断 ORLEANS0026 完全指南自定义 Grain 调用返回类型的 InvokableBaseType 映射失效与修复 导读 ORLEA后端微服务给 ImmortalWrt 插件换上中文皮肤给 ImmortalWrt 插件换上中文皮肤 装了个国外插件打开 LuCI浏览器里 192.168.1.1 那个网页管理页满屏英文当场劝退。别慌Imm操作系统嵌入式嵌入式OS固件网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

ctf-wiki 格式化字符串漏洞利用全解:从内存泄露到任意地址写入

ctf-wiki 格式化字符串漏洞利用全解:从内存泄露到任意地址写入

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 格式化字符串漏洞(Format String Vulnerability)是 Linux Pwn 入门到进阶绕不开的核…

2026/9/25 3:11:37 阅读更多 →
UG NX对象分析工具全解析:从模型体检到二次开发自动化

UG NX对象分析工具全解析:从模型体检到二次开发自动化

做UG NX这行久了,我越来越觉得,真正拉开交付档次的地方往往不在建模速度,而在交付前的“对象分析”环节。很多人习惯把UG NX里的对象分析工具当成一组可有可无的辅助命令,其实这是个大误会。这套工具不只是一个“测量器”&#xf…

2026/9/25 3:11:37 阅读更多 →
release-it 干运行(Dry Run)指南:安全预览版本发布流程与命令执行

release-it 干运行(Dry Run)指南:安全预览版本发布流程与命令执行

开发工具DevOps 【免费下载链接】release-it 🚀 Automate versioning and package publishing 项目地址: https://gitcode.com/gh_mirrors/re/release-it 点击查看 免费下载 导读 release-it 是一款自动化版本号管理、Git 标签/提交、Changelog 生成与…

2026/9/25 3:10:37 阅读更多 →

最新新闻

新疆价钱合理的石墨水泥基改性聚氨酯复合防火保温板厂家避坑挑选指南

新疆价钱合理的石墨水泥基改性聚氨酯复合防火保温板厂家避坑挑选指南

在新疆做外墙保温、墙体保温工程,挑选石墨水泥基改性聚氨酯复合防火保温板厂家,最怕遇到价格虚高、质量不稳、交付延期、检测不合格这些问题,不少施工方都踩过小厂家的坑:要么报价看着低,实际拿到的产品偷工减料厚度不…

2026/9/25 3:52:02 阅读更多 →
天达快修规模怎么样,成立多久了

天达快修规模怎么样,成立多久了

把握民生运维发展方向,践行本土服务行业使命 民生运维领域的发展需求与行业价值民生设备运维服务,是和城市居民日常生活、中小商户日常经营绑定在一起的基础服务领域,承载着保障城市生活正常运转的核心作用。伴随居民生活水平提升&#xff0c…

2026/9/25 3:52:02 阅读更多 →
PaddleNLP 大规模中文语料预训练数据处理实战:以 WuDaoCorpus2.0 Base 200GB 为例

PaddleNLP 大规模中文语料预训练数据处理实战:以 WuDaoCorpus2.0 Base 200GB 为例

人工智能大模型预训练微调LoRARLHF强化学习分布式训练 【免费下载链接】PaddleNLP Easy-to-use and powerful LLM and SLM library with awesome model zoo. 项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP 点击查看 免费下载 导读 本文基于 PaddleNLP 仓…

2026/9/25 3:52:02 阅读更多 →
EasyWeChat 6.x 微信支付模块实战指南:初始化、API 调用、签名验证与回调处理

EasyWeChat 6.x 微信支付模块实战指南:初始化、API 调用、签名验证与回调处理

后端即时通讯 【免费下载链接】easywechat 📦 一个 PHP 微信 SDK 项目地址: https://gitcode.com/gh_mirrors/ea/easywechat 点击查看 免费下载 本篇指南聚焦 EasyWeChat 6.x 的微信支付(Pay)模块,覆盖从商户资质初始…

2026/9/25 3:52:02 阅读更多 →
Humanizer 的 LetterCasing 枚举详解:Title、AllCaps、LowerCase 与 Sentence 四种字符串大小写转换

Humanizer 的 LetterCasing 枚举详解:Title、AllCaps、LowerCase 与 Sentence 四种字符串大小写转换

开发工具 【免费下载链接】Humanizer Humanizer meets all your .NET needs for manipulating and displaying strings, enums, dates, times, timespans, numbers and quantities 项目地址: https://gitcode.com/gh_mirrors/hu/Humanizer 点击查看 免费下载 导读 …

2026/9/25 3:52:02 阅读更多 →
基于STM32的实验室消防预警系统:原理图、仿真与代码全开源

基于STM32的实验室消防预警系统:原理图、仿真与代码全开源

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 3:51:02 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →