Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        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();
            }));
        }