网站建设公司擅自关闭客户网络,宁波机械加工网,友情链接seo,wordpress 创建表很多年前#xff0c;windows workflow foundation还叫WWF#xff0c;而直译过来的名称让很多人以为它就是用来开发工作流或者干脆就是审批流的。
博主当年还是个懵懂的少年#xff0c;却也知道微软不会大力推一个面向如此具象的业务场景的技术#xff0c;于是特地找了一本…很多年前windows workflow foundation还叫WWF而直译过来的名称让很多人以为它就是用来开发工作流或者干脆就是审批流的。
博主当年还是个懵懂的少年却也知道微软不会大力推一个面向如此具象的业务场景的技术于是特地找了一本《WF本质论》当看到“程序即数据”这个论断时被深深震撼了。可能这只是作者的随意一写但当时正是泛型方法、lamda表达式、匿名委托啥的开始出现的时候作者的这一说法在某种程度上暗合了博主平常的编程思想。于是逻辑与数据算法与结构它们之间的界限在我眼里在我心里开始以更诡异的方式模糊了起来。
然而之后并未在工作上使用过WF因此博主也就不再关注此项技术。如今重新翻看突然发现官方的Workflow Team blog最近的更新都是两年前了网上的资料都是N年前的而且大多数是教大家怎么用。为何要用何处要用基本上没有太多介绍。到了现如今似乎也被微软抛弃了为此我还专门在博问里提了下也没人知道具体详情。
很少有看到专门使用wf进行开发的场景好用与否在小众的群体里抽样得出结论也就显得不怎么可靠了。似乎wf在sharepoint环境中用起来比较协调由于博主对sharepoint没有研究过所以对此不好评论。如若在纯代码环境下开发用wf自带的一套activity能画出看似清晰的流程图然而关键的和外部交互的环节却仍然避免不了需要考虑的因素大部分仍然需要代码去完成而各种条件下流程的走向使用activity的条件判断又让人觉得没有直接写代码来的方便比如ifelse用代码可能只要在一个方法体中就能完成而在流程图中却要使用两个activity如果其中再有交互跳转的话流程图的复杂程度未必在代码之下而后期维护也难说容易。
不妨非恶意的揣测经过几年时间微软慢慢觉得将代码间原本隐晦的逻辑关系抽离出来变成看得见的“流程”对程序员来说未必有多大的意义于是就减少了这方面的投入。当然你也可以认为wf已经很成熟了不过即使如此博主还是倾向认为此种所谓成熟是因为找不到改进的方向虽然“成熟”但不怎么好用。
butwf并非一无是处wf的bookmark书签机制是最区别于传统开发的特性个人认为也是最重要的特性。传统开发时我们时不常的会遇到这种情形判断流程是否走到特定环节以便决定下一步操作其实就是判断A\B\C\D\...等排列组合变数它们之间可能也是相互关联的只要流程没有继续流转那么每次启动都要进行同样的判断是否可以在各变数满足条件时生成一个标签当我们发现这个标签存在时就可以去找标签对应的环节就能马上进行后续操作了。实际开发中我们常设置一个冗余的标识字段起这个作用。但单个字段未必能完全标识一个环节而且也显得不够直观。wf中的bookmark与上述的标签类似本质上它是对委托异步回调炉火纯青的应用更自然的描述是在变数尚未符合条件时的一种“等待”常用于与外部交互在流程设计时也不会像传统代码操作查询、判断、更新标签那样显得突兀这些操作wf都在更底层帮我们处理了。
开头说到微软推出wf的本意并非用于开发业务上的工作流我们甚至可以将任意有逻辑顺序的一段代码“wf化”然而从业务场景来讲wf的很多概念能映射其中毕竟抽象和具象都逃不离流程二字。因此项目前期或工期紧张需要快速开发的时候wf也能帮助程序员梳理流程——在[对]业务流程尚不清晰的时候你会发现这非常有用——就算日后摈弃wf大部分代码都可以复用而由于各环节明确的流转关系代码重构也较为容易。博主就是因为这个原因在目前的一个项目中使用了wf下面我会简单介绍相关要点和个人理解。
版本控制也是它的一个亮点这个就不细说了。
由于本人对wf钻研不深所述难免有误请各位同学批评指正。另本文并不展示任何项目代码只以行业一般流程展示博主在开发过程中的思索。 本项目主干是一手房置购流程涉及到认筹、认购、成交、租赁四个环节需求简化后如下图是用visio画的画得很挫一直没找到实线弧形箭头
1、环节流转 2、流转时需审核可跨级审核若申请人本身是最后环节审核人则直接通过 3、审核不通过时将退回发起环节审核过程中申请人也可撤消此次申请 4、单个环节也有不同状态比如认购有已认购和退认购两种状态此类状态转换也需要审核而内容变更也需要审核 5、同时至多只能存在一个审核中事项。比如认购内容变更审核中时就不能发起转成交申请。
这是一个比较典型的工作流场景。刚接手这个项目时我考虑用传统方式开发不多久我发现自己在各种环节、状态、标识、互斥项间晕头转向一团浆糊。即使流程图已经很清晰明白地摆在面前将之赋予代码和数据并使看似毫无关联的各块内容按照预期逻辑运行似乎显得相当困难。特别是产品有时会一脸无奈地跟你说认筹也可以转租赁业务说的隔天又说不需要了。要维系代码之间“隐秘”的逻辑关系并快速应对需求变更随着业务复杂度的提高难度也更快的升高。
当然对于熟练工来说这点难度不算什么毕竟我们每天都在做这种工作——将业务流程转化成层层调用的代码——我们还可以祭出设计模式、AOP、IOC、ORM啥的让代码看起来更清fu晰za。我们一直在接受面向对象、面向架构、面向服务的训练而缺少真正面向流程编程的经验我想这才是为什么微软当年会推广wf的原因此为病句意为强调。
wf有三种内置流程顺序流、Flowchart、StateMachine。本项目可考虑后两种博主选择的是StateMachine整出来的主流程如下 看上去很凌乱只能怪vs自带的wf设计器没有visio好用其实上图就是第1幅流程图的wf版本。
再来看下每条连线的逻辑以认购环节出去的连线为例触发器如下 可以看到流程会依据外部信号决定下一步骤流到下一个环节转成交、转租赁或者是退认购这里还有变更的情况图中没放出来。最终会根据流程中各变量和参数值选择某个路线 任何类型的审核不通过or申请人撤销就回到当前环节和之前我们用visio画的流程图一样直观。不过这里有个问题。博主刚接触statemachine时选择entry还是trigger放置业务逻辑比较困惑当时认为两者皆可毕竟最终只是通过condition来指定状态的转变方向用不着care condition在哪里生成博主当时认为微软设计statemachine时是将trigger当做bookmark activity的容器以便于与外部决策进行交互当然我们也可以将bookmark放在entry中说到底这就是一个规范问题不必深究。然而博主发现若流程从一个state流向同一个state——即state指向自己——其实前后两个环节已不是同一个“实例”了 因此原state的变量等状态不会保存entry会再次执行。为了避免这种状况有停留在当前状态的情形应该将业务逻辑写在entry中不进入到触发后的流转因为一旦流转就算流转回当前状态也是先出再进。当然如果当前状态没有需要保留的信息写在trigger亦可。
再来看看审核到底发生了什么。 这个更不用我多说了不过我还是要多说两句。虽然上图很清晰地还原了对应的需求但未必是最合适的做法我最后还是将这些逻辑统一到一个activity里。前面说过我们能将绝大多数多行代码wf化but复杂的流程需要借助wf一个简单方法就能搞定的逻辑如果还硬要画出几道条条理理来那就是偏执了。话说写程序的大多数人都是偏执的非黑即白我没说错吧
对于前述需求的第5条单条wf流程天生就能满足不需要我们做额外的coding。思考一下若几个审核可以并行或流程流转到下一环节后上一环节需要变更了怎么办用多流程或子流程试试吧。 代码活动中普通的成员变量持久化在bookmark恢复后值丢失不可用若需要在持久化前后保持变量值应该使用Variable或应用Serializable特性两者都是博主推测没试过。
有些事务不能失败了就全部回退重来比如单据状态从a-b经过多位领导审批同意后结果因为最后一步提交不成功让单据回退到a重审这种情况只能将最后一步重新提交实际开发中可将此类“脏提交”定时再提交。
一个activity里可create多个bookmark当所有bookmark都恢复执行后流程才继续往下。
wf在流程流转过程中并不能返回数据给调用者比如发起认筹转认购的申请调用ResumeBookmark方法并不能知晓是直接通过还是等待审核需要另外查数据库得到状态结果。当然可以使用WorkflowApplication.Extensions与外部程序交互但不能满足某些场景比如客户端调用webapi服务端action发起流程action自然需要知道流程跑完后暂停or完结的结果是什么并返回给客户端这时候WorkflowApplication.Extensions就然并卵了除非改写底层比如让wf能取消action的后续执行并将结果数据写入http连接。
流程改版or外部依赖项变化wf下要么新旧版本并行要么研究如何将旧版本迁移到新版本很多情况下没有纯代码控制来得方便。
博主观点同步SaaS并非wf较适用的场景wf适用于异步消息推送场景比如外卖点餐状态、快递状态、银行资金流转等客户端并不等待马上的结果而是事项状态改变时接收服务端消息即可。当然硬要在同步环境下使用也可以此时需要更松散的设计和底层框架的配合以适应wf“封闭式流程”的特点。 需继续研究的点
1、有些流程大同小异能否封装成一个可配置的流程在设计时进行简单的参数配置就能显示不同的流程步骤比如a状态可转为b、c状态而b只能转c状态那么ab在转换到下一个环节的判断逻辑就有少许差异了。这貌似只能通过元数据实现。
2、InstanceOwner网上实在找不到更多关于它的介绍目前可知是用于多宿主环境下宿主对wf实例的lock。没找到给实例赋值InstanceOwner的直接途径网上找到的基本上是下面两行代码
InstanceView view instore.Execute(instore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
instore.DefaultInstanceOwner view.InstanceOwner;
不知是基于什么因素考虑总是觉得不太理解是什么意思为什么要以及以这种方式设置DefaultInstanceOwner如果不设置的话那么下面这行代码运行时就会报错
WorkflowApplicationInstance instance WorkflowApplication.GetInstance(Guid.Parse(flowinstance.InstanceID), instore);
框架应该完全可以在底层就自动给我们处理InstanceOwner相关的东东比如下面这句在没设置DefaultInstanceOwner的时候就不会报错
wfApp.Load(Guid.Parse(flowinstance.InstanceID));
如何能设置自己的InstanceOwner呢maybe可以通过InstanceOwnerMetadata。 CreateWorkflowOwnerCommand createCommand new CreateWorkflowOwnerCommand()
{InstanceOwnerMetadata {{ WorkflowHostTypeName, new InstanceValue(WFInstanceScopeName) },}
}; 不过设置完了怎么获取又是一个问题有兴趣的朋友可以研究下这个文件也可以在官方WF_WCF_示例里找到。总之持久化这趟子水够深。 其它参考资料
Loading persisted workflow instances with WorkflowApplication