Visual Studio 2012 - Search for Circular Links Effectively

Currently, if I want to check circular references inside a solution, I choose Architecture - Generate Dependency Graph - For Solution . Then, from the new tab that opens, select Layout - Analyzers - Circular References Analyzer . Finally, if I expand from individual assemblies and have circular links, I see them highlighted in red on the graph, and they also appear as warnings in the error list.

Since I intend to define circular references even between methods of the same class, this is quite error prone and takes a lot of time on a moderately large code base.

I would like to know if there is a way to get all warnings at once without the need to expand nodes, or perhaps turn on selection for parent nodes so that I can only deploy on assemblies that undoubtedly contain circular references.

NDepend should be able to help, but I prefer to keep everything as simple as possible, so I am always afraid to accept additional tools.

+8
visual-studio visual-studio-2012 circular-reference circular-dependency
source share
1 answer

Yes NDepend can find circular links. Effectively let me explain how this could be easier than you might think (Disclaimer: I am one of the developers of NDepend). While you can find the namespace dependency loop outside the box, but as I explain below, it is easy to find loops between types in the namespace or type methods as well.

There is a default C # LINQ rule that lists namespace dependency loops. This loop can then be exported to a dependency graph or dependency matrix. Here's a screenshot of a rule based on the Roslyn CTP code, June 2012 (only 16 milliseconds were spent notice). He discovered 11 different cycles, and as shown in the screenshot, you can expand each cycle and export the cycle to the chart:

Rule to match namespace dependency cycle

Here is a dependency graph of a loop of 7 namespaces. Note that this looks more complicated than just the classic O-ring loop. The key point here is that from any of these namespaces, you can span everything else. This is a generalized concept of a cycle (confusion).

Match namespace dependency cycle Graph

The default C # LINQ rule code, which lists dependency cycles between namespaces, at first glance looks scary. But a C # developer should figure this out in a few minutes and then easily adapt it to find some kind of dependency cycle.

For example, to find methods of the same cycle types (instead of the namespace of the same assembly cycles ), it is almost as simple as replacing all the words in the namespace with the method and assembly by type.

 // <Name>Avoid methods of a type to be in cycles</Name> warnif count > 0 from t in Application.Types .Where(t => t.ContainsMethodDependencyCycle != null && t.ContainsMethodDependencyCycle.Value) // Optimization: restreint methods set // A method involved in a cycle necessarily have a null Level. let methodsSuspect = t.Methods.Where(m => m.Level == null) // hashset is used to avoid iterating again on methods already caught in a cycle. let hashset = new HashSet<IMethod>() from suspect in methodsSuspect // By commenting this line, the query matches all methods involved in a cycle. where !hashset.Contains(suspect) // Define 2 code metrics // - Methods depth of is using indirectly the suspect method. // - Methods depth of is used by the suspect method indirectly. // Note: for direct usage the depth is equal to 1. let methodsUserDepth = methodsSuspect.DepthOfIsUsing(suspect) let methodsUsedDepth = methodsSuspect.DepthOfIsUsedBy(suspect) // Select methods that are both using and used by methodSuspect let usersAndUsed = from n in methodsSuspect where methodsUserDepth[n] > 0 && methodsUsedDepth[n] > 0 select n where usersAndUsed.Count() > 0 // Here we've found method(s) both using and used by the suspect method. // A cycle involving the suspect method is found! let cycle = usersAndUsed.Append(suspect) // Fill hashset with methods in the cycle. // .ToArray() is needed to force the iterating process. let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray() select new { suspect, cycle } 

... and this is what the result of this rule looks like (it’s still possible to export the method loop to a dependency graph or matrix). Please note: since the number of methods and types is much larger than the number of namespaces and assemblies, this request took 10 seconds to work on a large code base such as Roslyn (instead of 16 ms for the namespace loop), so you may need to configure the execution timeout CQLinq query (default is 2 seconds).

types dependency cycles

To be complete, I noticed that the loop in most cases is triggered by several bidirectional links (for example, A uses B, B uses A). Therefore, removing bidirectional links is the first thing to do to break the loop. That's why we provided the default CQLinq rule. Avoid interdependent namespaces that can still be adapted to types or methods.

+6
source share

All Articles