Created
March 8, 2013 11:50
-
-
Save orient-man/5115935 to your computer and use it in GitHub Desktop.
Draws diagram of system modules based on assembly references.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text.RegularExpressions; | |
namespace Company.MvcApp.Utils | |
{ | |
public class DotModuleDiagram | |
{ | |
private readonly Regex moduleIncludeRegex = new Regex(@"^Company\.System\..*Core$"); | |
private readonly Regex moduleExcludeRegex = new Regex(@"^Company\.System\.Migrations\.Core$"); | |
private readonly Dictionary<Assembly, Assembly[]> moduleMap = | |
new Dictionary<Assembly, Assembly[]>(); | |
public string Draw() | |
{ | |
return Draw(Assembly.GetExecutingAssembly()); | |
} | |
public string Draw(Assembly rootModule) | |
{ | |
FindDependencies(rootModule); | |
return DrawModuleMap(); | |
} | |
private void FindDependencies(Assembly module) | |
{ | |
var name = module.GetName().Name; | |
if (moduleMap.Keys.Any(o => o.GetName().Name == name)) | |
return; | |
var dependencies = module | |
.GetReferencedAssemblies() | |
.Where(o => IsModule(o.Name)) | |
.OrderBy(o => o.Name) | |
.Select(Assembly.Load) | |
.ToArray(); | |
if (IsModule(name)) | |
moduleMap[module] = dependencies; | |
foreach (var dep in dependencies) | |
{ | |
FindDependencies(dep); | |
} | |
} | |
private bool IsModule(string moduleName) | |
{ | |
return moduleIncludeRegex.IsMatch(moduleName) && | |
!moduleExcludeRegex.IsMatch(moduleName); | |
} | |
private string DrawModuleMap() | |
{ | |
var modules = ""; | |
var relations = ""; | |
foreach (var m in moduleMap.Keys.OrderBy(o => o.GetName().Name)) | |
{ | |
var moduleName = FormatModuleName(m); | |
modules += FormatModule(moduleName, GetDomainObjects(m)); | |
relations = moduleMap[m] | |
.Aggregate( | |
relations, | |
(current, dep) => current + FormatRelation(moduleName, dep)); | |
} | |
return "digraph CompanySystem {\n" + | |
"\trankdir=LR\n" + | |
"\tnode [shape=plaintext]\n" + | |
"\tedge [arrowsize=0.75 fontsize=14 fontcolor=blue]\n\n" + | |
modules + "\n" + relations + "}"; | |
} | |
private static IEnumerable<string> GetDomainObjects(Assembly module) | |
{ | |
return module.GetTypes() | |
.Where(o => | |
typeof(IDomainObject) | |
.IsAssignableFrom(o) && o.IsInterface) | |
.OrderBy(o => o.Name) | |
.Select(o => o.Name.Substring(1)) | |
.DefaultIfEmpty(); | |
} | |
private static string FormatModule( | |
string name, IEnumerable<string> domainObjects) | |
{ | |
return "\t" + name + " [label=<\n" + | |
"<table border='0' cellborder='1' cellspacing='0' cellpadding='2'>\n" + | |
"\t<tr><td bgcolor='lightblue'>" + | |
name + "</td></tr>\n" + | |
"\t<tr><td><table border='0' cellborder='0' cellspacing='0' cellpadding='0'>\n" + | |
"\t\t<tr><td align='left'>" + | |
domainObjects | |
.Aggregate((current, domainObject) => | |
current + | |
"</td></tr>\n\t\t<tr><td align='left'>" + | |
domainObject) + | |
"</td></tr>\n\t</table></td></tr>\n" + | |
"</table>>];\n"; | |
} | |
private static string FormatRelation(string name, Assembly dep) | |
{ | |
return "\t" + name + " -> " + FormatModuleName(dep) + ";\n"; | |
} | |
private static string FormatModuleName(Assembly module) | |
{ | |
var assemblyName = module.GetName().Name; | |
var postfix = assemblyName.Replace("Company.System.", ""); | |
return postfix == "Core" ? "Core" : postfix.Replace(".Core", ""); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment