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 ...
随机推荐
- nyoj---快速查找素数
快速查找素数 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 现在给你一个正整数N,要你快速的找出在2.....N这些数里面所有的素数. 输入 给出一个正整数数N ...
- linux命令中的 < 和 |、>符号作用就解释
输出重定向比如输入一条命令,默认行为是将结果输出到屏幕.但有时候我们需要将输出的结果保存到文件,就可以用重定向.ps > ps.txt < 表示的是输入重定向的意思,就是把<后面跟的 ...
- 网站博客更换主机空间搬家:Discuz! X2.5老鹰主机搬家全过程
http://www.freehao123.com/discuz-x2-5-banjia/由于我放在hawkhost老鹰主机主机的部落论坛就要到期了,而老鹰主机的续费价格却是按照原价来的,没有任何优惠 ...
- AsyncTask与ProgressDialog使用笔记(安卓在背景运行耗时任务)
AsyncTask用在需要在ui线程中调用.在背景线程中执行耗时任务.并且在ui线程中返回结果的场合.下面就是一个在背景中运行的AsyncTask的实现DownloadDBTask, Android中 ...
- Java Web Project自定义错误页面,log4j记录日志。
创建记录日志的文件LoggerHelper.java: package com.wyp.helper; import org.apache.log4j.Logger; public class Log ...
- IIS7的应用程序池详细解析
在 IIS 7 中,应用程序池有两种运行模式:集成模式和经典模式.应用程序池模式会影响服务器处理托管代码请求的方式 在IIS 7中,添加一个应用程序或者单独的网站,默认会自动新建一个对应的“应用程序池 ...
- CView类的使用
首先我们来写一个样例: 1.建一个win32简单应用程序,不要觉得这样就不能写出MFC程序,由于是不是MFC程序取决于调没调MFC函数. 2. 删除入口函数.仅仅留下#include "st ...
- Sublime Text 无法使用Package Control或插件安装失败的解决方法
Sublime Text用了一年多了,公司搬家近期突然发现Package Control无法安装新插件了.尽管不影响原有功能的使用.还是要解决它.因为本人用Windows系统,仅仅讨论Windosw下 ...
- 哇塞!HTML5 实现的雨滴效果 CSS发抖
http://dreamsky.github.io/main/blog/rainy-day/ Rainy Day – 哇塞! HTML5 实现的雨滴效果 http://www.webhek.com/m ...
- Linux 普通进程 后台进程 守护进程(转)
一.普通进程与后台进程 默认情况下,进程是在前台运行的,这时就把shell给占据了,我们无法进行其它操作.对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个'& ...