Skip to content

Instantly share code, notes, and snippets.

@orient-man
Created March 8, 2013 11:50
Show Gist options
  • Save orient-man/5115935 to your computer and use it in GitHub Desktop.
Save orient-man/5115935 to your computer and use it in GitHub Desktop.
Draws diagram of system modules based on assembly references.
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