拓列排序
原理:
在图论中,拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
1.每个顶点出现且只出现一次。
2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
入度:在有向图中,箭头是具有方向的,从一个顶点指向另一个顶点,
这样一来,每个顶点被指向的箭头
出度:从这个顶点指出去的箭头个数,就是它的出度
可以看到他有一个依赖关系:
1.Module D 依赖于 Module E 与 Module B 。
2.Module E 依赖于 Module B 与 Module C 。
3.Module B 依赖于 Module A 与 Module C 。
4.Module C 依赖于 Module A 。
5.Module A 无依赖 。
这个就是一个 DAG 图,我们要得到它的拓扑排序,一个简单的步骤如下:
1.从 DAG 图中选择一个没有前驱的顶点并输出。
2.从 DAG 图中删除该顶点,以及以它为起点的有向边。
重复步骤 1、2 直到当前的 DAG 图为空,或者当前图不存在无前驱的顶点为止
拓扑排序c#代码
using System;
using System.Collections.Generic;
namespace 拓扑排序
{
internal class Program
{
static void Main(string[] args)
{
var moduleA = new Item("Module A");
//A依赖于B
var moduleB = new Item("Module B", moduleA);
//B依赖于C
var moduleC = new Item("Module C", moduleB);
//C依赖于D
var moduleD = new Item("Module D", moduleC);
//D依赖于E
var moduleE = new Item("Module E", moduleD);
//E依赖于A
moduleA.Dependencies = new Item[]
{
moduleE
};
var unsorted = new[] { moduleA, moduleB, moduleC, moduleD};
var sorted = Sort(unsorted, x => x.Dependencies);
foreach (var item in sorted)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
public static IList<T> Sort<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
{
var sorted = new List<T>();
var visited = new Dictionary<T, bool>();
foreach (var item in source)
{
Visit(item, getDependencies, sorted, visited);
}
return sorted;
}
public static void Visit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
{
bool inProcess;
var alreadyVisited = visited.TryGetValue(item, out inProcess);
// 如果已经访问该顶点,则直接返回
if (alreadyVisited)
{
// 如果处理的为当前节点,则说明存在循环引用
if (inProcess)
{
throw new ArgumentException("Cyclic dependency found.");
}
}
else
{
// 正在处理当前顶点
visited[item] = true;
// 获得所有依赖项
var dependencies = getDependencies(item);
// 如果依赖项集合不为空,遍历访问其依赖节点
if (dependencies != null)
{
foreach (var dependency in dependencies)
{
// 递归遍历访问
Visit(dependency, getDependencies, sorted, visited);
}
}
// 处理完成置为 false
visited[item] = false;
sorted.Add(item);
}
}
public class Item
{
// 条目名称
public string Name { get; private set; }
// 依赖项
public Item[] Dependencies { get; set; }
public Item(string name, params Item[] dependencies)
{
Name = name;
Dependencies = dependencies;
}
public override string ToString()
{
return Name;
}
}
}
}
拓扑排序解决工作流嵌套循环的问题
提出一个新需求工作流中有一个自动进出站。此时就需要用到拓扑排序来进行验证
public async Task<Guid> CreateOrUpdate(CreateOrUpdateOperationInput input)
{
if (input.Operation.Id.HasValue)
{
//判断工艺里面有无这个工序id
var Spec = _specManager.QueryAsNoTracking.Where(p => p.OperationId == input.Operation.Id).ToList();
foreach (var spec in Spec)
{
//工艺里面设置自动进出站 如果设置了就判断循环嵌套
if (spec.AutoMoveOut == true)
{
var WorkFlowSteps = _gworkflowManager.GetWorkflowStepQuery().Where(p => p.SpecBaseId == spec.SpecBaseId && p.SpecId == spec.Id).ToList();
foreach (var workFlowStep in WorkFlowSteps)
{
var workflow = await _workflowManager.QueryAsNoTracking.FirstOrDefaultAsync(p => p.Id == workFlowStep.WorkflowId);
if (workflow != null)
{
var workflowBase = await _workflowManager.BaseQueryAsNoTracking.FirstOrDefaultAsync(p => p.Id == workflow.WorkflowBaseId);
if (workflowBase != null) workflow.WorkflowBase = workflowBase;
var workflowInput = ObjectMapper.Map<WorkflowEditDto>(workflow);
workflowInput.WorkflowDesignInfo = await GetGWorkflowDesignInfo(workFlowStep.WorkflowId);
//判断工作流是否有循环 拿去工序去判断
//workflowInput 这个dto里面有整个工作流工序的节点
await CyclicWorkflow(workflowInput, input.Operation);
}
}
}
}
return await Update(input.Operation);
}
else
{
return await Create(input.Operation);
}
}
public async Task CyclicWorkflow(WorkflowEditDto input, OperationEditDto operation)
{
string workflowName = input.WorkflowBase.WorkflowName + ":" + input.WorkflowRevision;
List<Item> items = new List<Item>();
foreach (var nodes in input.WorkflowDesignInfo.Nodes)
{
var item = new Item(nodes.Key);
items.Add(item);
}
var index = 0;
foreach (var nodes in input.WorkflowDesignInfo.Nodes)
{
string a1 = nodes.StepBody.Inputs.FirstOrDefault().Value.Value.ToString();
var specid = Guid.Parse(a1.Substring(0, 36));
var spec = await _specManager.QueryAsNoTracking.FirstOrDefaultAsync(p => p.Id == specid);
//因为刚传进来的数据 并没有存入数据库。所以会对当前工艺的工序进行判断,如果是相当的,就走前端传过来的值进行判断。
if (spec.OperationId == operation.Id)
{
if (operation.UseQueue == true)
{
if (spec.AutoMoveOut == true && spec.AutoMoveIn == true)
{
foreach (var NextNodes in nodes.NextNodes)
{
var Parent = items.FirstOrDefault(x => x.Name == NextNodes.NodeId);
items[index].Dependencies = new Item[] {
Parent
};
}
}
}
else
{
if (spec.AutoMoveOut == true)
{
foreach (var NextNodes in nodes.NextNodes)
{
var Parent = items.FirstOrDefault(x => x.Name == NextNodes.NodeId);
items[index].Dependencies = new Item[] {
Parent
};
}
}
}
}
else
{
var UseQueue = _operationManager.QueryAsNoTracking.FirstOrDefault(x => x.Id == spec.OperationId).UseQueue;
if (UseQueue)
{
if (spec.AutoMoveOut == true && spec.AutoMoveIn == true)
{
foreach (var NextNodes in nodes.NextNodes)
{
var Parent = items.FirstOrDefault(x => x.Name == NextNodes.NodeId);
items[index].Dependencies = new Item[] {
Parent
};
}
}
}
else
{
if (spec.AutoMoveOut == true)
{
foreach (var NextNodes in nodes.NextNodes)
{
var Parent = items.FirstOrDefault(x => x.Name == NextNodes.NodeId);
items[index].Dependencies = new Item[] {
Parent
};
}
}
}
}
index++;
}
var sorted = Sort(items, x => x.Dependencies, workflowName);
}
private IList<T> Sort<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies, string workflowName)
{
var sorted = new List<T>();
var visited = new Dictionary<T, bool>();
foreach (var item in source)
{
Visit(item, getDependencies, sorted, visited, workflowName);
}
return sorted;
}
private void Visit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited, string workflowName)
{
bool inProcess;
var alreadyVisited = visited.TryGetValue(item, out inProcess);
// 如果已经访问该顶点,则直接返回
if (alreadyVisited)
{
// 如果处理的为当前节点,则说明存在循环引用
if (inProcess)
{
ThrowUserFriendlyError(L("WorkFlowError", workflowName));
}
}
else
{
// 正在处理当前顶点
visited[item] = true;
// 获得所有依赖项
var dependencies = getDependencies(item);
// 如果依赖项集合不为空,遍历访问其依赖节点
if (dependencies != null)
{
foreach (var dependency in dependencies)
{
// 递归遍历访问
Visit(dependency, getDependencies, sorted, visited, workflowName);
}
}
// 处理完成置为 false
visited[item] = false;
sorted.Add(item);
}
}
private class Item
{
// 条目名称
public string Name { get; private set; }
// 依赖项
public Item[] Dependencies { get; set; }
public Item(string name, params Item[] dependencies)
{
Name = name;
Dependencies = dependencies;
}
public override string ToString()
{
return Name;
}
}
#endregion 工作流判断是否存在自动进出站的循环
private async Task<WorkflowDesignInfo> GetGWorkflowDesignInfo(Guid workflowId)
{
var res = new GWorkflowDef();
res.Steps = await
_gworkflowManager.GetAllStepByDesignId(workflowId);
res.StepPaths = await _gworkflowManager.GetAllStepPathByDesignId(workflowId);
res.PathSelectors = await _gworkflowManager.GetAllStepPathSelectorByDesignId(workflowId);
var workflowNodes = WorkflowCoreHelper.Convert(res);
return new WorkflowDesignInfo()
{
Nodes = workflowNodes,
};
}
.StepPaths = await _gworkflowManager.GetAllStepPathByDesignId(workflowId);
res.PathSelectors = await _gworkflowManager.GetAllStepPathSelectorByDesignId(workflowId);
var workflowNodes = WorkflowCoreHelper.Convert(res);
return new WorkflowDesignInfo()
{
Nodes = workflowNodes,
};
}