拓列排序学习与实践解决工作流嵌套循环

拓列排序

原理:

在图论中,拓扑排序(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,
            };
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值