新闻详情

新闻详情

首页 / 资讯中心 / 详情

OrchardCore 工作流活动(Workflow Activity)开发完全指南:从基类、生命周期到显示驱动与表达式求值

发布时间:2026/9/27 9:01:34来源:尧图网络
OrchardCore 工作流活动(Workflow Activity)开发完全指南:从基类、生命周期到显示驱动与表达式求值
CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载导读本文是 Orchard Core 工作流模块中自定义活动Activity开发的实战参考基于仓库内OrchardCore.Workflows模块与OrchardCore.Workflows.Abstractions项目的真实源码编写。你将掌握任务型活动Task与事件型活动Event的区别与生命周期、GetProperty/SetProperty属性持久化机制、WorkflowExpressionT表达式Liquid/JavaScript的求值方式、ActivityDisplayDriver显示驱动的形状Shape约定以及如何通过AddActivity将活动注册进工作流运行时。读完本文你可以直接照着源码写出一个带编辑器界面、可暂停/恢复、可被外部事件唤醒的自定义工作流活动。从基类说起任务与事件的分工OrchardCore.Workflows.Abstractions/Activities目录下定义了活动的核心抽象。TaskActivityTActivity与EventActivity是两个最重要的基类其区别决定了活动在运行时的行为类型命名空间用途TaskActivityTActivityOrchardCore.Workflows.Activities类型化任务Name默认取类型名TaskActivity同上任务基类Activity, ITaskEventActivity同上事件基类Activity, IEventExecute返回Halt()ActivityDisplayDriverTActivityOrchardCore.Workflows.Display只提供缩略图与设计时形状ActivityDisplayDriverTActivity, TEditViewModel同上额外提供编辑形状与模型映射从源码看TaskActivity.cspublic abstract class TaskActivity : Activity, ITask; public abstract class TaskActivityTActivity : TaskActivity where TActivity : ITask { // 工作流定义中使用的技术名称默认就是类型的 Name public override string Name typeof(TActivity).Name; }即继承TaskActivityLogTask的活动其技术名称自动就是LogTask无需手工指定。而事件基类EventActivity.cs则直接覆盖了Executepublic abstract class EventActivity : Activity, IEvent { public override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityContext activityContext) { // 挂起工作流等待事件发生 return Halt(); } }这是任务与事件在行为上的本质分界任务执行一次即完成事件则把工作流挂起等待外部触发。Activity抽象类Activity.cs是两者的共同祖先它提供Properties一个JsonObject类型的属性袋bag用于持久化活动状态GetProperty/SetProperty辅助方法通过[CallerMemberName]自动以成员名为键读写Properties静态帮助方法Outcome/Outcomes/Halt/Noop用于构造执行结果与可能的端口集合虚方法GetPossibleOutcomes、CanExecute(Async)、Execute(Async)供子类按需重写。其中GetPropertyT的签名值得注意protected virtual T GetPropertyT(FuncT defaultValue null, [CallerMemberName] string name null) { var item Properties[name]; return item ! null ? item.ToObjectT() : defaultValue ! null ? defaultValue() : default; }Properties是以 JSON 形式存储的因此任何可 JSON 序列化的类型都可以安全地作为活动属性。任务Task生命周期五个关键阶段一个任务型活动从被设计师拖入画布到最终执行会依次经历以下阶段GetPossibleOutcomes设计器与运行时都会调用它来获知该活动有哪些输出端口portsCanExecuteAsync可选执行前的门卫gate返回false则跳过本次执行ExecuteAsync真正干活的入口返回Outcome(...)指示走向哪个端口如果返回了Halt()ResumeAsync稍后被调用活动挂起后由运行时在合适的时机恢复结果流转ActivityExecutionResult决定工作流下一步走向。以仓库中最简单的任务 LogTask.cs 为例它完整演示了任务活动的骨架public class LogTask : TaskActivityLogTask { private readonly ILogger _logger; private readonly IWorkflowExpressionEvaluator _expressionEvaluator; protected readonly IStringLocalizer S; public LogTask(ILoggerLogTask logger, IWorkflowExpressionEvaluator expressionEvaluator, IStringLocalizerLogTask localizer) { _logger logger; _expressionEvaluator expressionEvaluator; S localizer; } public override LocalizedString DisplayText S[Log Task]; public override LocalizedString Category S[Primitives]; public LogLevel LogLevel { get GetProperty(() LogLevel.Information); set SetProperty(value); } public WorkflowExpressionstring Text { get GetProperty(() new WorkflowExpressionstring()); set SetProperty(value); } public override IEnumerableOutcome GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) Outcome(S[Done]); public override async TaskActivityExecutionResult ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) { var text await _expressionEvaluator.EvaluateAsync(Text, workflowContext, null); var logLevel LogLevel; _logger.Log(logLevel, 0, text, null, (state, error) state.ToString()); return Outcome(Done); } }从中可以看到任务型活动的三个要点通过构造函数注入ILogger、IWorkflowExpressionEvaluator、IStringLocalizer等服务属性用GetProperty(() 默认值)/SetProperty模式声明保证未设置时返回非 null 的默认实例GetPossibleOutcomes返回本地化的端口名称ExecuteAsync返回对应的字符串端口名。事件Event生命周期挂起、唤醒与恢复事件型活动的生命周期与任务截然不同其核心是挂起-唤醒模式EventActivity.Execute返回Halt()——工作流挂起实例状态持久化到数据库外部代码触发事件例如调用IWorkflowManager.TriggerEventAsyncCanExecuteAsync决定当前挂起实例是否应当恢复Resume/ResumeAsync返回输出端口工作流继续向下执行。仓库中最典型的例子是 SignalEvent.cspublic class SignalEvent : EventActivity { public static string EventName nameof(SignalEvent); private readonly IWorkflowExpressionEvaluator _expressionEvaluator; protected readonly IStringLocalizer S; public override string Name EventName; public override LocalizedString DisplayText S[Signal Event]; public override LocalizedString Category S[HTTP]; public WorkflowExpressionstring SignalName { get GetProperty(() new WorkflowExpressionstring()); set SetProperty(value); } public override async Taskbool CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) { var signalName await _expressionEvaluator.EvaluateAsync(SignalName, workflowContext, null); return string.Equals(workflowContext.Input.GetValuestring(Signal), signalName, StringComparison.OrdinalIgnoreCase); } public override IEnumerableOutcome GetPossibleOutcomes(WorkflowExecutionContext workflowContext, ActivityContext activityContext) Outcome(S[Done]); public override ActivityExecutionResult Resume(WorkflowExecutionContext workflowContext, ActivityContext activityContext) Outcome(Done); }它的工作方式很精妙CanExecuteAsync把活动上配置的SignalName与外部传入的Input[Signal]做不区分大小写的比较作为是否该由本实例响应的门卫Resume直接返回Outcome(Done)表示事件命中后继续沿Done端口前进。外部代码如何触发IWorkflowManager接口IWorkflowManager.cs提供了入口TaskIEnumerableWorkflowExecutionContext TriggerEventAsync(string name, IDictionarystring, object input null, string correlationId null, bool isExclusive false, bool isAlwaysCorrelated false);还提供了一个便捷扩展方法把匿名对象转换为RouteValueDictionary作为 input 传入。correlationId用于把事件与特定业务实体如内容项 ID关联isExclusive控制是否只恢复一个匹配实例。另一个事件型活动的例子是 TimerEvent.cs它演示了带持久化状态的事件活动挂起后把StartedUtc写入PropertiesResumeAsync时用NCrontab解析CronExpression通过IClock计算下一次触发时间未到期就再次Halt()到期则返回Outcome(Done)。默认表达式为*/5 * * * *每 5 分钟并支持UseLocalTime开关把站点设置的时区纳入计算。这正是挂起状态在进程重启后依然有效的绝佳例证。属性持久化GetProperty / SetProperty 的底层机制活动属性持久化是活动开发中最常用的基础设施。其核心模式如下public WorkflowExpressionstring Text { get GetProperty(() new WorkflowExpressionstring()); set SetProperty(value); }需要记住的三条规则键是成员名通过[CallerMemberName]自动捕获属性名作为Properties字典的键无需手工指定以 JSON 存储属性被序列化进Properties并随工作流实例落库因此能跨越挂起/恢复乃至进程重启提供默认工厂GetProperty(() new ...)让未设置的属性返回合理的非 null 默认值避免空引用问题。注意GetPropertyT在不传默认工厂时会返回default引用类型为 null所以凡是会被直接使用的属性都建议提供默认值工厂这一点在TimerEvent.CronExpression默认*/5 * * * *、LogTask.LogLevel默认LogLevel.Information等源码中处处可见。表达式Expression让活动接受 Liquid 与 JavaScript面向用户的输入应当声明为WorkflowExpressionT这样工作流作者就可以在编辑器中写 Liquid 模板或 JavaScript 脚本。WorkflowExpressionTWorkflowExpression.cs本质上是包了一层原始模板字符串public class WorkflowExpressionT { public WorkflowExpression() { } public WorkflowExpression(string expression) { Expression expression; } public string Expression { get; set; } }求值时注入两个求值器IWorkflowExpressionEvaluatorIWorkflowExpressionEvaluator.cs用于Liquid表达式签名如下TaskT EvaluateAsyncT(WorkflowExpressionT expression, WorkflowExecutionContext workflowContext, TextEncoder encoder);IWorkflowScriptEvaluatorIWorkflowScriptEvaluator.cs用于JavaScript脚本支持传入作用域方法提供者TaskT EvaluateAsyncT(WorkflowExpressionT expression, WorkflowExecutionContext workflowContext, params IGlobalMethodProvider[] scopedMethodProviders);典型用法摘自 LogTask.csvar text await _expressionEvaluator.EvaluateAsync(Text, workflowContext, null);视图模型View Model通常暴露一个.Expression属性即原始模板字符串供编辑器直接绑定驱动层负责在模型与活动之间搬运。SetOutputTaskSetOutputTask.cs更进一步演示了语法切换它同时持有ValueJavaScript与LiquidValueLiquid两个表达式由Syntax属性WorkflowScriptSyntax.JavaScript/WorkflowScriptSyntax.Liquid决定用哪个求值器var value Syntax switch { WorkflowScriptSyntax.Liquid await _expressionEvaluator.EvaluateAsync(LiquidValue, workflowContext, null), WorkflowScriptSyntax.JavaScript await _scriptEvaluator.EvaluateAsync(Value, workflowContext), _ throw new NotSupportedException($The syntax {Syntax} isnt supported for SetOutputTask.) }; workflowContext.Output[OutputName] value;这就是一个写输出任务的完整实现把求值结果写入工作流上下文的Output字典。输出端口Outcomes与 ActivityExecutionResult端口是工作流连线的依据。活动需要在GetPossibleOutcomes中声明本地化显示并在Execute/Resume中按字符串名返回public override IEnumerableOutcome GetPossibleOutcomes(...) Outcome(S[Yes], S[No]); public override async TaskActivityExecutionResult ExecuteAsync(...) condition ? Outcome(Yes) : Outcome(No);从 Activity.cs 的源码可以看到Outcome系列帮助方法的完整形态Outcome(params LocalizedString[])返回IEnumerableOutcome用于声明Outcome(params string[])/Outcome(params IEnumerablestring)返回ActivityExecutionResult用于执行旧的Outcomes(...)重载均已被标记[Obsolete]新代码应使用Outcome。ActivityExecutionResultActivityExecutionResult.cs是活动的返回值类型Outcomes(params string[])/Outcome(...)—— 沿指定端口继续执行Halt()—— 返回ActivityExecutionResult.HaltedIsHalted true工作流挂起Noop()—— 返回ActivityExecutionResult.Empty不做任何事继续。其内部实现非常直观public class ActivityExecutionResult { public static readonly ActivityExecutionResult Empty new([]); public static readonly ActivityExecutionResult Halted new([]) { IsHalted true }; public IEnumerablestring Outcomes { get; private set; } public bool IsHalted { get; private set; } }工作流上下文数据Input / Output / PropertiesWorkflowExecutionContextWorkflowExecutionContext.cs是活动与整个工作流实例之间的数据通道三个字典分工明确public sealed class WorkflowExecutionContext { public IDictionarystring, object Input { get; } // 来自发起方 public IDictionarystring, object Output { get; } // 回传给发起方 public IDictionarystring, object Properties { get; } // 跨活动共享的状态 }读取输入与写出输出的惯用法var signal workflowContext.Input.GetValuestring(Signal); // 读输入 workflowContext.Output[OutputName] value; // 写输出此外从构造函数可以看出上下文还携带WorkflowType、Workflow实例、已执行活动栈ExecutedActivities、LastResult以及按ActivityId索引的活动字典为活动提供了完整的运行环境。SignalEvent正是通过workflowContext.Input.GetValuestring(Signal)读取外部信号而SetOutputTask通过workflowContext.Output[OutputName] value把结果回传给发起方——读写两端的范例都在仓库源码中。显示驱动Display Driver内部机制每个活动通常配套一个显示驱动负责在设计师画布上呈现缩略图、设计形状以及编辑表单。基类ActivityDisplayDriverTActivity与ActivityDisplayDriverTActivity, TEditViewModelActivityDisplayDriver.cs从活动的技术名称推导出三种形状类型{ActivityName}_Fields_Thumbnail {ActivityName}_Fields_Design {ActivityName}_Fields_Edit源码中用typeof(TActivity).Name静态缓存了ActivityName并拼接出s_thumbnailShapeType与s_designShapeType带视图模型的派生类再拼接出s_editShapeTypeprotected static readonly string ActivityName typeof(TActivity).Name; private static readonly string s_thumbnailShapeType ${ActivityName}_Fields_Thumbnail; private static readonly string s_designShapeType ${ActivityName}_Fields_Design; // 派生类中 private static readonly string s_editShapeType ${ActivityName}_Fields_Edit;DisplayAsync把缩略图形状放在Thumbnail/Content位置、设计形状放在Design/Content位置Edit用InitializeTEditViewModel(s_editShapeType, viewModel EditActivityAsync(activity, viewModel))初始化编辑形状。需要重写的映射钩子只有两个方向protected override void EditActivity(TActivity activity, TEditViewModel model) { /* activity - model */ } protected override void UpdateActivity(TEditViewModel model, TActivity activity) { /* model - activity */ } // 异步变体EditActivityAsync, UpdateActivityAsync值得注意UpdateAsync已经替你完成了context.Updater.TryUpdateModelAsync(viewModel, Prefix)模型绑定随后调用UpdateActivity最后返回Edit(...)重新渲染编辑界面。也就是说你只需要写双向映射逻辑绑定与形状渲染都由基类完成。默认的EditActivity/UpdateActivity是空实现未重写时编辑界面只显示空白表单所以务必备齐这对钩子。注册活动AddActivity 与服务装配活动写好后需要在模块的Startup.ConfigureServices中注册services.AddActivityLogTask, LogTaskDisplayDriver(); services.AddActivitySignalEvent, SignalEventDisplayDriver();AddActivity一次完成三件事注册活动类型、注册其显示驱动、把活动加入WorkflowOptions。它要求两个类型参数——活动本身与配套的显示驱动无驱动时可传只有一个泛型参数的版本。仓库中的实际用法可参考 OrchardCore.Workflows/Startup.cs注册WorkflowFaultEvent等与 Http/Startup.cs[Feature(OrchardCore.Workflows.Http)] public sealed class Startup : StartupBase { public override void ConfigureServices(IServiceCollection services) { // ... services.AddActivityHttpRequestEvent, HttpRequestEventDisplayDriver(); services.AddActivityHttpRedirectTask, HttpRedirectTaskDisplayDriver(); services.AddActivitySignalEvent, SignalEventDisplayDriver(); // ... } }注意依赖 HTTP 功能或特定 Feature 的活动应放在带[RequireFeatures(...)]如[Feature(OrchardCore.Workflows.Http)]的独立Startup中注册仓库对OrchardCore.Workflows.Http正是这样处理的——这保证了功能开关关闭时相关活动不会被暴露到设计器中。仓库中的实战范例清单下表汇总了可直接对照学习的仓库范例覆盖了从最小任务到带持久化状态事件的各类写法活动文件示范要点LogTaskActivities/LogTask.cs最小任务、表达式求值、日志输出NotifyTask 驱动OrchardCore.Workflows/Activities 与 Drivers任务与显示驱动的成对写法SetOutputTaskActivities/SetOutputTask.cs写入Output、语法切换SetPropertyTask同上目录写入Properties跨活动共享状态SignalEventHttp/Activities/SignalEvent.cs事件门卫 恢复TimerEventTimers/TimerEvent.cs带持久化状态的事件、Cron 调度动手写一个活动的完整检查清单综合以上源码分析一个完整的自定义活动需要覆盖这些环节选择基类一次性执行选TaskActivityTActivity需要挂起等外部触发选EventActivity声明属性全部用GetProperty(() 默认值)/SetProperty模式让属性可持久化、不返回 null设计输入用户可编辑的输入用WorkflowExpressionT注入IWorkflowExpressionEvaluatorLiquid和/或IWorkflowScriptEvaluatorJS求值声明端口在GetPossibleOutcomes中用Outcome(S[...])本地化返回端口实现执行逻辑在ExecuteAsync中干活并返回Outcome(...)需要挂起时返回Halt()之后实现ResumeAsync读写上下文用workflowContext.Input.GetValueT(key)读、workflowContext.Output[key] value写编写驱动继承ActivityDisplayDriverTActivity, TEditViewModel实现EditActivity/UpdateActivity或异步变体配合{Name}_Fields_Edit.cshtml等视图注册在Startup.ConfigureServices中调用services.AddActivityTActivity, TDisplayDriver()依赖特定功能的放到带[RequireFeatures(...)]的Startup中。按照这个清单对照LogTask、SetOutputTask、SignalEvent、TimerEvent四个范例源码你就能完整掌握 Orchard Core 工作流活动开发的全部要点。赞分享CMS后端Web框架【免费下载链接】OrchardCoreOrchard Core is an open-source modular and multi-tenant application framework built with ASP.NET Core, and a content management system (CMS) built on top of that framework.项目地址https://gitcode.com/gh_mirrors/or/OrchardCore点击查看免费下载相关推荐OrchardCore 自定义工作流活动开发指南从 Task/Event 到显示驱动与注册OrchardCore 自定义工作流活动开发指南从 Task/Event 到显示驱动与注册 工作流是 OrchardCore 中最具代表性的模块化能力之一一CMS后端Web框架Argo Workflows LifecycleHook 完整指南条件表达式驱动的生命周期钩子Argo Workflows LifecycleHook 完整指南条件表达式驱动的生命周期钩子 LifecycleHook 是 Argo Workflows云原生容器编排工作流自动化任务调度后端CANN/GE aclmdlQuerySize函数aclmdlQuerySizea nameZH CN_TOPIC_0000001312641793 /a 产品支持情况a namesection人工智能深度学习模型编译模型优化编译器Ascend上一篇hashsigs-ts工程实践tsupVitest构建并开源TypeScript密码学库的完整流程下一篇字符的视觉革命ASCII艺术生成器探索指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

乌鲁木齐全屋定制推荐:适合大宅意式设计的品牌分析 2026/9/27 9:57:37

乌鲁木齐全屋定制推荐:适合大宅意式设计的品牌分析

乌鲁木齐全屋定制指南:大宅意式设计的品牌选择与考量在乌鲁木齐进行家庭装修规划时,获取一份客观的乌鲁木齐全屋定制推荐参考清单,往往是业主开启装修旅程的重要一步。需要明确的是,本文旨在基于公开的市场信息、品牌定位差异以及…

阅读更多 →
Operit 数据救援:Preferences DataStore 配置文件健康检测与保全优先修复实战 2026/9/27 9:57:37

Operit 数据救援:Preferences DataStore 配置文件健康检测与保全优先修复实战

AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆 【免费下载链接】Operit The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent 项目地址: https://gitcode.com/gh_mirrors/o…

阅读更多 →
wordpress上传至哪个目录下免费工具推荐 2026/9/27 9:57:37

wordpress上传至哪个目录下免费工具推荐

1个目录搞懂WordPress上传路径:图解步骤避坑指南 找建站公司,最怕的就是花大价钱却被忽悠装到错误目录,导致网站打不开或无法上传文件。别急,这套图解步骤能帮你一眼看穿真相,省下冤枉钱。…

阅读更多 →
CTF-Wiki 密碼學安全僞隨機數生成器(CSPRNG)完全指南:從 next-bit test 到 CTF 實戰 2026/9/27 9:57:30

CTF-Wiki 密碼學安全僞隨機數生成器(CSPRNG)完全指南:從 next-bit test 到 CTF 實戰

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 導讀 本文基於 CTF-Wiki 密碼學專欄中 csprng.md 一文,系統梳理密碼學安全僞隨機數生成器&…

阅读更多 →
你好 普通的自己 2026/9/27 9:57:30

你好 普通的自己

不必急于求成,每个人都有自己的节奏。路上有疲惫、有挫折都是常态,暂时的停滞不代表失败。那些默默付出、咬牙坚持的日子,都在悄悄积攒力量。不用和别人比较,专注走好自己脚下的路就好。遇到难题可以短暂休息,但不要轻…

阅读更多 →
OpCore Simplify 新手教程:OpenCore EFI 自动配置与硬件兼容性检查指南 2026/9/27 9:57:30

OpCore Simplify 新手教程:OpenCore EFI 自动配置与硬件兼容性检查指南

OpCore Simplify 新手教程:OpenCore EFI 自动配置与硬件兼容性检查指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore Simplify 是…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉