WF追忆
前一阵子学习了一下工作流,现在写个总结记录一下这个过程。要弄工作流,首先就要有个界面来画图,做web的,没办法,只能选择javascript和silverlight,找来找去,最后用了Shareidea的和Workflow11的界面,在此对他们表示感谢,界面是在Shareidea上面进行的修改,把Workflow11的很多东西也揉进来了,最后合成的一个杂交体。但是最后因为要玩hadoop,要清理磁盘空间,把工程给误删了,直到现在才发现。。我3个月的业余时间完成的代码全部被干掉了,已经无法挽回了,只能做一下追忆罢了,现在把残存的一些代码给发上来,算是纪念一下。
/// <summary> /// 解析工作流 /// </summary> /// <param name="xml"></param> /// <returns></returns> private string AnalyzerWorkFlow(string xml) { #region 读取xml信息,生成linq to xml 信息 string xaml = string.Empty; //把字符串解析成XElement Byte[] b = System.Text.UTF8Encoding.UTF8.GetBytes(xml); XElement element = XElement.Load(System.Xml.XmlReader.Create(new MemoryStream(b))); #endregion #region 保存模板 //模板信息 var template = new Template(); template.Name = element.Attribute(XName.Get("Name")).Value; template.Guid = Guid.Parse(element.Attribute(XName.Get("UniqueID")).Value); template.Version = element.Attribute(XName.Get("Version")).Value; template.Templates = xml; template.UpdateTime = DateTime.Now; #endregion #region 初始化变量 //获取所有的activity和rule var nodes = from item in element.Descendants("Activity") select item; var rules = from item in element.Descendants("Rule") select item; //建立flowchart,从第一个节点开始遍历整个图 Flowchart flowchart = new Flowchart() { DisplayName = template.Name }; IActivity activity; FlowNode preStep = new FlowStep(); FlowNode nextStep; FlowSwitch<string> flowSwitch; XElement xele; IEnumerable<XElement> linkedRules; string uniqueId = string.Empty; string activityId = string.Empty; //实例化开始节点 var firstNode = nodes.First((node) => node.Attribute(XName.Get("Type")).Value == "INITIAL"); var startActivity = new StartActivity(firstNode.Attribute(XName.Get("UniqueID")).Value); startActivity.DisplayName = firstNode.Attribute(XName.Get("ActivityName")).Value; ((FlowStep)preStep).Action = startActivity; flowchart.Nodes.Add(preStep); flowchart.StartNode = preStep; //设置一个栈,把东西put进去;设置一个字典,把创建过的活动id,以及位置记录进去 var stack = new Stack<IActivity>(); var dic = new Dictionary<string, int>(); stack.Push(startActivity); dic.Add(startActivity.ActivityId, flowchart.Nodes.Count - ); #endregion #region 遍历生成flowchart图形 ) { activity = stack.Pop(); activityId = activity.ActivityId; linkedRules = from rule in rules where rule.Attribute(XName.Get("BeginActivityUniqueID")).Value == activityId select rule; //节点被清空之后,重新定位 if (preStep == null) { preStep = flowchart.Nodes[dic[activityId]]; } //后续活动有多个 ) { //条件自动判断路径活动 if (activity is ConditionActivity) { #region 判断节点,根据用户事先预置的条件自动判断选择下一步节点 string trueActivityId = ((ConditionActivity)activity).Property.ActivityForTrue; string falseActivityId = ((ConditionActivity)activity).Property.ActivityForFalse; //把false写在前面是因为栈是倒序的,遍历节点按照倒序的方式来遍历了,但是在生成xaml的时候, //生成出来的xaml的条件中的true节点的后续节点在后面呢,还没建立,所以无法引用到后续的节点 //只有前面的节点先建立了,后面的节点才能使用前面的节点的引用 if (!dic.ContainsKey(falseActivityId)) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == falseActivityId); activity = GetActivityByType(xele); nextStep = CreateFlowNodeByType(activity); flowchart.Nodes.Add(nextStep); dic.Add(falseActivityId, flowchart.Nodes.Count - ); ((FlowDecision)preStep).False = nextStep; stack.Push(activity); } else { ((FlowDecision)preStep).False = flowchart.Nodes[(dic[falseActivityId])]; } if (!dic.ContainsKey(trueActivityId)) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == trueActivityId); activity = GetActivityByType(xele); nextStep = CreateFlowNodeByType(activity); flowchart.Nodes.Add(nextStep); dic.Add(trueActivityId, flowchart.Nodes.Count - ); ((FlowDecision)preStep).True = nextStep; preStep = nextStep; stack.Push(activity); } else { ((FlowDecision)preStep).True = flowchart.Nodes[(dic[trueActivityId])]; } #endregion } //用户选择路径活动 else if (activity is ActiveActivity) { //后续活动类型为串行 ) { #region 串行活动,处理人选择下一步操作 flowSwitch = new FlowSwitch<string>(); flowSwitch.Expression = new VisualBasicValue<string>() { ExpressionText = "NextWay" }; flowSwitch.Default = flowchart.StartNode; foreach (XElement linkedRule in linkedRules) { uniqueId = linkedRule.Attribute(XName.Get("EndActivityUniqueID")).Value; if (!dic.ContainsKey(uniqueId)) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == uniqueId); activity = GetActivityByType(xele); nextStep = CreateFlowNodeByType(activity); flowchart.Nodes.Add(nextStep); dic.Add(activity.ActivityId, flowchart.Nodes.Count() - ); flowSwitch.Cases.Add(uniqueId.ToLower(), nextStep); preStep = nextStep; stack.Push(activity); } else { flowSwitch.Cases.Add(uniqueId.ToLower(), flowchart.Nodes[(dic[uniqueId])]); } } flowchart.Nodes.Add(flowSwitch); //通过activityId找到节点在flowchart中的位置,然后设置它的next节点 ((FlowStep)flowchart.Nodes[dic[activityId]]).Next = flowchart.Nodes[flowchart.Nodes.Count - ]; #endregion } //后续活动类型为并行活动 else { #region 并行活动 var parallel = new Parallel(); parallel.CompletionCondition = false; Switch<string> witch; Sequence seq; ; //取得汇合节点的id var joinPointId = GetJoinPointId(rules, activityId); foreach (XElement linkedRule in linkedRules) { uniqueId = linkedRule.Attribute(XName.Get("EndActivityUniqueID")).Value; //如果连线直接连着 if (uniqueId == joinPointId) continue; xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == uniqueId); activity = GetActivityByType(xele); currentCount = stack.Count; seq = new Sequence(); seq.Activities.Add((Activity)activity); stack.Push(activity); while (stack.Count > currentCount) { activity = stack.Pop(); uniqueId = activity.ActivityId; var seqRules = from rule in rules where rule.Attribute(XName.Get("BeginActivityUniqueID")).Value == uniqueId select rule; ) { witch = new Switch<string>(); witch.Expression = new VisualBasicValue<string>() { ExpressionText = "NextWay" }; foreach (XElement seqRule in seqRules) { var caseId = seqRule.Attribute("EndActivityUniqueID").Value; if (caseId != joinPointId) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == caseId); activity = GetActivityByType(xele); witch.Cases.Add(caseId.ToLower(), (Activity)activity); stack.Push(activity); } } seq.Activities.Add(witch); } ) { uniqueId = seqRules.First().Attribute("EndActivityUniqueID").Value; if (uniqueId != joinPointId) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == uniqueId); activity = GetActivityByType(xele); seq.Activities.Add((Activity)activity); stack.Push(activity); } } } parallel.Branches.Add(seq); } //并行节点作为flowchart中的一个节点来处理 nextStep = new FlowStep(); ((FlowStep)nextStep).Action = parallel; ((FlowStep)preStep).Next = nextStep; flowchart.Nodes.Add(nextStep); preStep = nextStep; //处理完并行结构之后,添加汇合节点 xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == uniqueId); activity = GetActivityByType(xele); nextStep = CreateFlowNodeByType(activity); flowchart.Nodes.Add(nextStep); dic.Add(activity.ActivityId, flowchart.Nodes.Count() - ); ((FlowStep)preStep).Next = nextStep; preStep = nextStep; stack.Push(activity); #endregion } } } //后续活动只有一个 ) { #region 后续只有一个活动节点 uniqueId = linkedRules.First().Attribute("EndActivityUniqueID").Value; if (!dic.ContainsKey(uniqueId)) { xele = nodes.First((node) => node.Attribute(XName.Get("UniqueID")).Value == uniqueId); activity = GetActivityByType(xele); nextStep = CreateFlowNodeByType(activity); flowchart.Nodes.Add(nextStep); dic.Add(activity.ActivityId, flowchart.Nodes.Count() - ); ((FlowStep)preStep).Next = nextStep; preStep = nextStep; stack.Push(activity); } else { //活动已存在,通过dic字典中记录的位置,将“前节点”的Next指针指向它 ((FlowStep)flowchart.Nodes[dic[activityId]]).Next = flowchart.Nodes[dic[uniqueId]]; //((FlowStep)preStep).Next = flowchart.Nodes.ElementAt(dic[uniqueId]); } #endregion } //没有后续节点 else { //如果没有后续节点,则把“前节点”清空,然后重新定位前节点 preStep = null; } } #endregion #region 将图形转成xaml格式的文件,并且保存 try { xaml = GetXmlFromActiviyBuilder(flowchart); //xaml = XamlServices.Save(flowchart); template.XamlTemplate = xaml; using (var scope = new System.Transactions.TransactionScope()) { TemplateService.AddTemplate(template); CreateRoad(rules, template.Guid); scope.Complete(); } } catch (Exception ex) { xaml = ex.Message; } #endregion return xaml; } #region 辅助函数 /// <summary> /// 通过ActivityBuilder给添加一个传入参数,否则无法传值。 /// </summary> /// <param name="flowchart"></param> /// <returns></returns> private string GetXmlFromActiviyBuilder(Flowchart flowchart) { ActivityBuilder ab = new ActivityBuilder(); ab.Implementation = flowchart; ab.Properties.Add(new DynamicActivityProperty() { Name = "Entity", Type = typeof(InOutArgument<object>) }); ab.Properties.Add(new DynamicActivityProperty() { Name = "NextWay", Type = typeof(InOutArgument<string>) }); StringBuilder stringBuilder = new StringBuilder(); StringWriter stringWriter = new StringWriter(stringBuilder); XamlSchemaContext xamlSchemaContext = new XamlSchemaContext(); XamlXmlWriter xamlXmlWriter = new XamlXmlWriter(stringWriter, xamlSchemaContext); XamlWriter xamlWriter = ActivityXamlServices.CreateBuilderWriter(xamlXmlWriter); XamlServices.Save(xamlWriter, ab); return stringBuilder.ToString(); } /// <summary> /// 创建路径线路图,用于用户打开单据时候,生成操作按钮 /// </summary> /// <param name="rules"></param> /// <param name="templateId"></param> private void CreateRoad(IEnumerable<XElement> rules, Guid templateId) { var roadList = new List<object>(); TemplateRoads road = null; foreach (var rule in rules) { road = new TemplateRoads(); road.Id = Guid.NewGuid(); road.Source = rule.Attribute(XName.Get("BeginActivityUniqueID")).Value; road.Target = rule.Attribute(XName.Get("EndActivityUniqueID")).Value; road.Name = rule.Attribute(XName.Get("RuleName")).Value; road.TemplateId = templateId; //这个是控制他们的顺序的 road.Zindex = Convert.ToInt32(rule.Attribute(XName.Get("ZIndex")).Value); roadList.Add(road); } DBHelper.WriteDataTableToDb(Common.FillDataTable(roadList)); } /// <summary> /// 创建路径线路图,用于用户打开单据时候,生成操作按钮 /// </summary> /// <param name="activitys"></param> /// <param name="templateId"></param> private void CreateActivitys(IEnumerable<XElement> activitys, Guid templateId) { var roadList = new List<object>(); TemplateRoads road; foreach (var activity in activitys) { road = new TemplateRoads(); road.Id = Guid.NewGuid(); road.Source = activity.Attribute(XName.Get("BeginActivityUniqueID")).Value; road.Target = activity.Attribute(XName.Get("EndActivityUniqueID")).Value; road.Name = activity.Attribute(XName.Get("RuleName")).Value; road.TemplateId = templateId; //这个是控制他们的顺序的 road.Zindex = Convert.ToInt32(activity.Attribute(XName.Get("ZIndex")).Value); roadList.Add(road); } DBHelper.WriteDataTableToDb(Common.FillDataTable(roadList)); } /// <summary> /// 通过开始分叉节点的id,计算流程汇合的节点的id /// </summary> /// <param name="rules">所有的线路</param> /// <param name="startNodeId">开始节点的id</param> /// <returns></returns> private string GetJoinPointId(IEnumerable<XElement> rules, string startNodeId) { var linkedRules = from rule in rules where rule.Attribute(XName.Get("BeginActivityUniqueID")).Value == startNodeId select rule; ) { var list = new List<IEnumerable<XElement>>(); var uniqueId = string.Empty; ; i < linkedRules.Count(); i++) { uniqueId = linkedRules.ElementAt(i).Attribute(XName.Get("EndActivityUniqueID")).Value; list.Add(GetAfterRules(rules, uniqueId).ToList()); } //计算交集 IEnumerable<XElement> result = null; foreach (IEnumerable<XElement> item in list) { if (result == null) { result = item; } else { result = result.Intersect(item); } } ) { return result.First().Attribute(XName.Get("BeginActivityUniqueID")).Value; } } return null; } /// <summary> /// 递归查找某个节点的后续连线 /// </summary> /// <param name="rules">所有的线路</param> /// <param name="startNodeId">开始节点</param> /// <returns></returns> private IEnumerable<XElement> GetAfterRules(IEnumerable<XElement> rules, string startNodeId) { var linkedRules = from rule in rules where rule.Attribute(XName.Get("BeginActivityUniqueID")).Value == startNodeId select rule; return linkedRules.ToList().Concat(linkedRules.ToList().SelectMany( t => GetAfterRules(rules, t.Attribute(XName.Get("EndActivityUniqueID")).Value))); } /// <summary> /// 根据activity的类型,返回相应的FlowNode节点类型 /// </summary> /// <param name="activity"></param> /// <returns></returns> private FlowNode CreateFlowNodeByType(IActivity activity) { if (activity is ConditionActivity) { return new FlowDecision() { Condition = activity as ConditionActivity }; } else { return new FlowStep() { Action = (Activity)activity }; } } /// <summary> /// 通过类型来创建活动 /// </summary> /// <param name="element">节点元素</param> /// <returns>返回对应的活动</returns> private IActivity GetActivityByType(XElement element) { var uniqueId = element.Attribute(XName.Get("UniqueID")).Value; var type = element.Attribute(XName.Get("Type")).Value; //取得属性节点 var property = element.FirstNode.NextNode as XElement; dynamic propertyObj = null; switch (type) { case "INITIAL": return new StartActivity(uniqueId); case "COMPLETION": return new EndActivity(uniqueId); case "INTERACTION": propertyObj = new WFActivityProperty(); XmlUtils.XMLToModel(property.ToString(), propertyObj); return new ActiveActivity(uniqueId, propertyObj); case "CONDITION": propertyObj = new WFConditionProperty(); XmlUtils.XMLToModel(property.ToString(), propertyObj); return new ConditionActivity(uniqueId, propertyObj); default: return null; } } #endregion
这是生成xaml的算法。还想说点什么,但是也没有代码了,说啥啊。。 无代码无真相。。 就说点关于自定义节点的问题吧,用flowchart来构图的话,会遇到一个问题,就是并行节点的处理,在我上面的算法当中,是把并行节点开始到并行结束节点之间的节点视作一个FlowNode,但是如果需要并行之后还有并行这些更复杂的工作流节点的话,可以考虑用NativeActivity,下面是我在写动态修改工作流实例的时候在官网上面找到的一些代码,它是一个并行节点的实现,我觉得是一个很重大的发现。
public sealed class MyParallelActivity : NativeActivity { Collection<Activity> branches; public Collection<Activity> Branches { get { if (this.branches == null) { this.branches = new Collection<Activity>(); } return this.branches; } } protected override void Execute(NativeActivityContext context) { foreach (Activity branch in this.Branches) { context.ScheduleActivity(branch); } } protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity) { metadata.AllowUpdateInsideThisActivity(); } protected override void UpdateInstance(NativeActivityUpdateContext updateContext) { // look for new branches that need to be scheduled foreach (Activity branch in this.Branches) { // NativeActivityUpdateContext.IsNewlyAdded looks for children not present in the original (pre-update) activity if (updateContext.IsNewlyAdded(branch)) { updateContext.ScheduleActivity(branch); } } } }
注意Execute方法中的一句话:context.ScheduleActivity(branch); --->调度执行子活动,看到这一句之后,我确实是很兴奋的,因为之前也想过自己写一个完整的Activity,但是苦于不知道怎么执行它的下一个活动。所以如果想重新实现的朋友请继承NativeActivity来实现,因为除了原生的类型之后,WF只支持NativeActivity动态修改后面的流程。
再想想,还有什么没交代的。。。想到了一个,就是判断条件的,比如switch的这种开关的判断条件,它的判断条件可以是一个CodeActivity<string>,我们可以继承重写一个,然后就可以在Execute方法当中写判断的代码了,这里主要是要用到CodeDom来在运行时动态计算结果。就说这么多了,没代码,什么都讲不清楚了,说了也白说。
这个故事告诉我,我需要一个保存代码的服务器了。。。
最后把我残存的那一点代码放出来吧,在CSDN上下载http://download.csdn.net/detail/cenyuhaiwork/5670947。
WF追忆的更多相关文章
- 转《WF编程》笔记目录
<WF编程>笔记目录 2008-03-18 09:33 by Windie Chai, 26803 阅读, 49 评论, 收藏, 编辑 WF笔记开始 <WF编程>系列之0 - ...
- wf(七)(手把手包会)
这个demo中我们将用If/Else逻辑加到工作流用来展示不同的message通过自定义的条件. 如果name的字符数是奇数,第一个单词就输出“Greeting”否则输出“Hello”. 1. 在Sa ...
- wf(五)
测试工作流: 运用wf(四)的solution: 创建单元测试项目: 1.选择HelloWorkflow解决方案,右键选择添加新建项目:选择单元测试模板,命名为HelloWorkflow.Tests. ...
- wf(四)
我们已经在c#和xaml上编写了工作流,或者有的人会觉得在xaml上编写的workflow没什么优点,然而其实xaml其实具有一些很特别的优势. 1. xaml支持工作流设计器,c#不支持: 2. x ...
- wf(三)
前言: 到现在我们可以看到,WF4包含一个.xmal 文件的设计器和一个调用活动的runtime.当你创建自己的工作流的时候,你是同时也创建了一个活动, 因为活动是一个继承System.Activit ...
- WF(二)
步骤一: 运用WF(一)中创建好的solution 重命名Workflow1.xaml,变为SayHello.xaml 并在属性窗口设置名称为HelloWorkflow.SayHello,如下图: ( ...
- [WF] Quickstart Sample
[WF] Quickstart Sample 前言 Workflow Foundation(WF),总是给人一种很有用.可是却不知道怎么用的印象.这主要是因为前置的功课太多.要整合很多底层知识,才能完 ...
- CS中调用微软自带com组件实现音频视频播放(wf/wpf)
1.mp3播放器:工具箱中右键,在弹出的菜单中选择“选择项”,添加“com组件”,选择名称“windows Media Player",点击确定就会在工具箱新增一个“windows Medi ...
- WF 快速入门
WF(Windows Workflow Foundation ,中文译为:Windows工作流基础)是一种基于更高级抽象概念的编程语言,适合于实现业务流程.虽然可以通过使用图形化的工具(Workflo ...
随机推荐
- HDUOJ---------Kia's Calculation
Kia's Calculation Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
- Swift和Objective-C混编的注意啦
文/仁伯安(授权) 原文链接:http://www.jianshu.com/p/2ed48b954612 前言 Swift已推出数年,与Objective-C相比Swift的语言机制及使用简易程度上更 ...
- php 内存管理
内存是计算机⾮常关键的部件之⼀,是暂时存储程序以及数据的空间,CPU只有有限的寄存器可以⽤于存储计算数据,⽽⼤部分的数据都是存储在内存中的,程序运⾏都是在内存中进⾏的.和CPU计算能⼒⼀样, 内存也是 ...
- AspxPivotGrid和WebChartControl数据联动处理日志
AspxPivotGrid具有很好的表格样式体验,WebChartControl也是个很内容丰富的做图控件,我希望实现的功能是这样的, 处理题库统计分析图表,用户点AspxPivotGrid绑定知识点 ...
- python中logging模块的使用
一.基本用法 只需要基本的配置,就可以使用了. import logging def fun2(): logging.basicConfig(filename="fun2.log" ...
- MySQL数据分组GROUP BY 和HAVING
对于分组的理解,可以这样:对GROUP BY子句后面跟随的列名进行分组,然后对每一个分组而不是整个表进行操作. 举例如下:在产品表中,检索每一个供应商提供的商品的数量. mysql> SELEC ...
- HDU 2256 Problem of Precision(矩阵高速幂)
题目地址:HDU 2256 思路: (sqrt(2)+sqrt(3))^2*n=(5+2*sqrt(6))^n; 这时要注意到(5+2*sqrt(6))^n总能够表示成an+bn*sqrt(6); a ...
- python搭建简易服务器实例参考
有关python搭建简易服务器的方法. 需求分析: 省油宝用户数 已经破了6000,原有的静态报表 已经变得臃肿不堪, 每次打开都要缓上半天,甚至浏览器直接挂掉 采用python搭建一个最最简易的 w ...
- js操作checkbox(复选框)的方法总结
收集了一些用js代码操作checkbox复选框的代码,分享出来,供需要的朋友参考: <script> //复选框checkbox 处理方法 //搜集整理 www.jbxue.com fun ...
- 移动端自动化测试 -- appium 之Desired Capabilities与 定位控件
一.Desired Capabilities Desired Capabilities 在启动 session 的时候是必须提供的. Desired Capabilities 本质上是以 key va ...