private static Func <StronglyConnectedComponent <T>, DependencyCycle <T> > BuildGetDependencyCycleFromStronglyConnectedComponent <T>(IEqualityComparer <T> valueComparer, IEnumerable <StronglyConnectedComponent <T> > asList) { var vertexComparer = new VertexValueComparer <T>(valueComparer); var byVertex = (from c in asList from v in c select new { c, v }) .GroupBy(k => k.v, v => v.c, vertexComparer) .ToDictionary(k => k.Key, v => v.ToHashSet()); var cycleByComponent = new Dictionary <StronglyConnectedComponent <T>, DependencyCycle <T> >(); DependencyCycle <T> GetCycle(StronglyConnectedComponent <T> sc) { if (cycleByComponent.TryGetValue(sc, out var result)) { return(result); } var dependencies = sc.SelectMany(t => t.Dependencies).SelectMany(d => byVertex[d]); var sets = new HashSet <DependencyCycle <T> >(); DependencyCycle <T> cycle; cycleByComponent.Add(sc, cycle = new DependencyCycle <T>(sc.Select(t => t.Value).ToHashSet(valueComparer), sets)); sets.UnionWith(dependencies.Select(GetCycle).Where(t => t != cycle)); return(cycle); } return(GetCycle); }
private static IEnumerable <DependencyCycle <T> > VisitCyclesCore <T>(IEnumerable <DependencyCycle <T> > cycles, Action cancelled, IDictionary <DependencyCycle <T>, DependencyCycle <T> > cache, VisitCycleHandler <T> visit, IEqualityComparer <HashSet <DependencyCycle <T> > > setComparer) { foreach (var cycle in cycles) { if (cache.TryGetValue(cycle, out var newCycle)) { yield return(cycle); continue; } var dependenciesChanged = false; var dependencies = cycle.Dependencies as HashSet <DependencyCycle <T> > ?? cycle.Dependencies.ToHashSet(); if (dependencies.Count > 0) { var wasCancelled = false; var dependenciesSet = VisitCyclesCore(dependencies, () => wasCancelled = true, cache, visit, setComparer) .ToHashSet(); if (wasCancelled) { cancelled(); yield break; } if (!setComparer.Equals(dependenciesSet, dependencies)) { dependenciesChanged = true; dependencies = dependenciesSet; } } newCycle = visit(cycle, dependenciesChanged, dependencies); if (newCycle == null) { cancelled(); yield break; } if (newCycle == cycle && dependenciesChanged) { newCycle = new DependencyCycle <T>(cycle.Contents, dependencies); } cache.Add(cycle, newCycle); yield return(newCycle); } }
/// <summary> /// Allows to merge cyclic dependencies into a single instance of the source element type. /// </summary> /// <param name="components">Required. A sequence of dependency components as returned by <see cref="DependencyResolver.DetectCycles{T}"/>.</param> /// <param name="mergeCycle" >Not required. A delegate that take the cyclic component to merge and returns the merged instance of <see cref="T"/>. /// Will throw a <see cref="CyclicDependenciesDetectedException"/> when a cycle is found but <see cref="mergeCycle"/> is null.</param> public static IEnumerable <T> MergeCyclicDependencies <T>(this IEnumerable <DependencyCycle <T> > components, MergeCycleHandler <T> mergeCycle = null) { var asList = components.AsListInternal(); if (!asList.Any(t => t.IsCyclic)) { return(asList.ConvertAllInternal(t => t.Contents.Single())); } if (mergeCycle == null) { mergeCycle = delegate { throw new CyclicDependenciesDetectedException( "A cyclic dependency has been detected. This method cannot continue without a value for mergeCycle."); } } ; var mergedValues = new Dictionary <DependencyCycle <T>, T>(); asList.VisitCycles((cycle, changed, dependencies) => { var hasCycles = false; bool NeedsToRun() { cycle.Dependencies.VisitCycles((c, x, set) => { if (c.IsCyclic) { hasCycles = true; return(null); } return(c); }) // ReSharper disable once ReturnValueOfPureMethodIsNotUsed .Count(); return(hasCycles); } if (!cycle.IsCyclic && !NeedsToRun()) { return(cycle); } if (mergedValues.TryGetValue(cycle, out var result)) { return(cycle); } T GetMerged(DependencyCycle <T> c) { mergedValues.TryGetValue(c, out var r); return(r); } if (changed || hasCycles) { var newCycle = new DependencyCycle <T>(cycle.Contents, dependencies); mergedValues.Add(newCycle, result = mergeCycle(newCycle, GetMerged)); mergedValues[cycle] = result; return(newCycle); } mergedValues.Add(cycle, mergeCycle(cycle, GetMerged)); return(cycle); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed }).Count(); return(asList.ConvertAllInternal(c => { if (mergedValues.TryGetValue(c, out var merged)) { return merged; } return c.Contents.Single(); })); }