前一阵子学习了一下工作流,现在写个总结记录一下这个过程。要弄工作流,首先就要有个界面来画图,做web的,没办法,只能选择javascript和silverlight,找来找去,最后用了Shareidea的和Workflow11的界面,在此对他们表示感谢,界面是在Shareidea上面进行的修改,把Workflow11的很多东西也揉进来了,最后合成的一个杂交体。但是最后因为要玩hadoop,要清理磁盘空间,把工程给误删了,直到现在才发现。。我3个月的业余时间完成的代码全部被干掉了,已经无法挽回了,只能做一下追忆罢了,现在把残存的一些代码给发上来,算是纪念一下。
代码语言:js复制 /// <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 - 1);
#endregion
#region 遍历生成flowchart图形
while (stack.Count > 0)
{
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 (linkedRules.Count() > 1)
{
//条件自动判断路径活动
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 - 1);
((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 - 1);
((FlowDecision)preStep).True = nextStep;
preStep = nextStep;
stack.Push(activity);
}
else
{
((FlowDecision)preStep).True = flowchart.Nodes[(dic[trueActivityId])];
}
#endregion
}
//用户选择路径活动
else if (activity is ActiveActivity)
{
//后续活动类型为串行
if (((ActiveActivity)activity).Property.after_type == 1)
{
#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() - 1);
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 - 1];
#endregion
}
//后续活动类型为并行活动
else
{
#region 并行活动
var parallel = new Parallel();
parallel.CompletionCondition = false;
Switch<string> witch;
Sequence seq;
var currentCount = 0;
//取得汇合节点的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;
if (seqRules.Count() > 1)
{
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);
}
else if (seqRules.Count() == 1)
{
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() - 1);
((FlowStep)preStep).Next = nextStep;
preStep = nextStep;
stack.Push(activity);
#endregion
}
}
}
//后续活动只有一个
else if (linkedRules.Count() == 1)
{
#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() - 1);
((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;
if (linkedRules.Count() > 1)
{
var list = new List<IEnumerable<XElement>>();
var uniqueId = string.Empty;
for (int i = 0; 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);
}
}
if (result != null && result.Count() > 0)
{
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,下面是我在写动态修改工作流实例的时候在官网上面找到的一些代码,它是一个并行节点的实现,我觉得是一个很重大的发现。
代码语言:javascript复制 1 public sealed class MyParallelActivity : NativeActivity
2 {
3 Collection<Activity> branches;
4
5 public Collection<Activity> Branches
6 {
7 get
8 {
9 if (this.branches == null)
10 {
11 this.branches = new Collection<Activity>();
12 }
13 return this.branches;
14 }
15 }
16
17 protected override void Execute(NativeActivityContext context)
18 {
19 foreach (Activity branch in this.Branches)
20 {
21 context.ScheduleActivity(branch);
22 }
23 }
24
25 protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
26 {
27 metadata.AllowUpdateInsideThisActivity();
28 }
29
30 protected override void UpdateInstance(NativeActivityUpdateContext updateContext)
31 {
32 // look for new branches that need to be scheduled
33 foreach (Activity branch in this.Branches)
34 {
35 // NativeActivityUpdateContext.IsNewlyAdded looks for children not present in the original (pre-update) activity
36 if (updateContext.IsNewlyAdded(branch))
37 {
38 updateContext.ScheduleActivity(branch);
39 }
40 }
41 }
42 }
注意Execute方法中的一句话:context.ScheduleActivity(branch); --->调度执行子活动,看到这一句之后,我确实是很兴奋的,因为之前也想过自己写一个完整的Activity,但是苦于不知道怎么执行它的下一个活动。所以如果想重新实现的朋友请继承NativeActivity来实现,因为除了原生的类型之后,WF只支持NativeActivity动态修改后面的流程。
再想想,还有什么没交代的。。。想到了一个,就是判断条件的,比如switch的这种开关的判断条件,它的判断条件可以是一个CodeActivity<string>,我们可以继承重写一个,然后就可以在Execute方法当中写判断的代码了,这里主要是要用到CodeDom来在运行时动态计算结果。就说这么多了,没代码,什么都讲不清楚了,说了也白说。
这个故事告诉我,我需要一个保存代码的服务器了。。。
最后把我残存的那一点代码放出来吧,在CSDN上下载http://download.csdn.net/detail/cenyuhaiwork/5670947。