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 ...
随机推荐
- Mac下搭建svn服务器和XCode配置svn
先打开命令行终端. 1.创建svn repository svnadmin create /yourpath/svnroot/repository 2.配置svn用户权限. / yourpath /s ...
- eclipse svn登陆用户保存信息删除
win7系统 eclipse svn saveinfo path:磁盘:\%用户名%\AppData\Roaming\Subversion\auth\svn.simple 例如:D:\Users\用户 ...
- iOS开发Xcode8需要注意的那些坑
现在在苹果的官网上,我们已经可以下载到Xcode8的GM版本了,加上9.14日凌晨,苹果就要正式推出iOS10系统的推送了,在此之际,iOS10的适配已经迫在眉睫啦,不知道Xcode8 beat版本, ...
- 验证码在后台的编写,并实现点击验证码图片时时发生更新 C# 项目发布到IIS后不能用log4net写日志
验证码在后台的编写,并实现点击验证码图片时时发生更新 验证码在软件中的地位越来越重要,有效防止这种问题对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试:下面就是实现验证码的基本步骤: ...
- vue学习01
vue学习01 1. 创建一个Vue实例官网-学习-教程-安装-(开发/生产版本)-与jQuery的引用相似 <!DOCTYPE html> <html> <head ...
- OAF_OAF控件系列3 - Poplist的实现(案例)
2014-06-02 Created By BaoXinjian
- asp.net2.0导出pdf文件完美解决方案【转载】
asp.net2.0导出pdf文件完美解决方案 作者:清清月儿 PDF简介:PDF(Portable Document Format)文件格式是Adobe公司开发的电子文件格式.这种文件格式与操作系统 ...
- 怎么样快速完整备份和压缩 很大的 sqlserver 1TB 数据库 -摘自网络
How to increase SQL Database Full Backup speed using compression and Solid State Disks The SQL 2008 ...
- Spring Boot(一):初步认识
简介 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置 ...
- Linux环境下搭建测试环境(LAMP详细说明)
一.安装虚拟机与CentOS7 传送门:https://www.cnblogs.com/mrgavin/p/9372393.html 注意:以下安装,我都是用的root权限. 二.安装Apache1. ...