Here’s part eight of (at least) 10 Advanced Windsor Tricks.
A frequent complaint people have when they first get to grips with IoC Containers is that they find it hard to tell how their application composes at runtime. What’s needed is a clear description of the tree of dependencies in your application. This dependency ‘graph’ is built at registration time and Windsor exposes its dependency graph via the GraphNodes collection on the Kernel. The following class recursively walks down the GraphNodes tree and outputs the application’s complete component graph:
using System; using System.IO; using Castle.Core; using Castle.Windsor; namespace Suteki.Common.Windsor { public class DependencyGraphWriter { private readonly IWindsorContainer container; private TextWriter writer; public DependencyGraphWriter(IWindsorContainer container, TextWriter writer) { this.container = container; this.writer = writer; } public void Output() { var graphNodes = container.Kernel.GraphNodes; foreach (var graphNode in graphNodes) { if (graphNode.Dependers.Length != 0) continue; Console.WriteLine(); WalkGraph(graphNode, 0); } } private void WalkGraph(IVertex node, int level) { var componentModel = node as ComponentModel; if (componentModel != null) { writer.WriteLine("{0}{1} -> {2}", new string('\t', level), componentModel.Service.FullName, componentModel.Implementation.FullName); } foreach (var childNode in node.Adjacencies) { WalkGraph(childNode, level + 1); } } } }
I usually have a test fixture that asserts various container registrations. Using the class above it’s easy to add a little explicit test that outputs the application’s graph:
[Test, Explicit] public void Output_dependency_graph() { var dependencyGraphWriter = new DependencyGraphWriter(container, Console.Out); dependencyGraphWriter.Output(); }
Here’s an small part of the output for Suteki Shop:
Suteki.Shop.Controllers.OrderStatusController -> Suteki.Shop.Controllers.OrderStatusController Suteki.Common.Repositories.IRepository`1 -> Suteki.Common.Repositories.Repository`1 Suteki.Common.Repositories.IDataContextProvider -> Suteki.Common.Repositories.DataContextProvider Suteki.Common.Repositories.IConnectionStringProvider -> Suteki.Common.Repositories.ConnectionStringProvider Suteki.Shop.Services.IUserService -> Suteki.Shop.Services.UserService Suteki.Common.Repositories.IRepository`1 -> Suteki.Common.Repositories.Repository`1 Suteki.Common.Repositories.IDataContextProvider -> Suteki.Common.Repositories.DataContextProvider Suteki.Common.Repositories.IConnectionStringProvider -> Suteki.Common.Repositories.ConnectionStringProvider Suteki.Shop.Services.IFormsAuthentication -> Suteki.Shop.Services.FormsAuthenticationWrapper Suteki.Shop.Services.IEmailService -> Suteki.Shop.Services.EmailService Suteki.Common.Services.IEmailBuilder -> Suteki.Common.Services.EmailBuilder Suteki.Common.Services.IEmailSender -> Suteki.Common.Services.EmailSenderLogger Suteki.Common.Services.IEmailSender -> Suteki.Common.Services.NullEmailSender Castle.Core.Logging.ILogger -> Castle.Services.Logging.Log4netIntegration.Log4netLogger Suteki.Shop.Services.IBaseControllerService -> Suteki.Shop.Services.BaseControllerService Suteki.Common.Repositories.IRepository`1 -> Suteki.Common.Repositories.Repository`1 Suteki.Common.Repositories.IDataContextProvider -> Suteki.Common.Repositories.DataContextProvider Suteki.Common.Repositories.IConnectionStringProvider -> Suteki.Common.Repositories.ConnectionStringProvider
Hi Mike, this code didn't seem to give the output that you showed. I had to change a couple of things to make it work:
ReplyDeletepublic class DependencyGraphWriter
{
private readonly IWindsorContainer _container;
private readonly TextWriter _writer;
public DependencyGraphWriter(IWindsorContainer container, TextWriter writer)
{
_container = container;
_writer = writer;
}
public void Output()
{
var graphNodes = _container.Kernel.GraphNodes;
foreach (var graphNode in graphNodes)
{
_writer.WriteLine();
WalkGraph(graphNode, 0);
}
}
private void WalkGraph(GraphNode node, int level)
{
var componentModel = node as ComponentModel;
if (componentModel != null)
{
_writer.WriteLine(
"{0}{1} -> {2}",
new string('\t', level),
componentModel.ComponentName,
componentModel.Implementation.FullName);
}
foreach (var childNode in node.Dependents)
{
WalkGraph(childNode, level + 1);
}
}
}
I encountered a problem where one of Dependents was node itself and the graph traversal was locking in infinite loop. The solution was to rewrite WalkGraph method into following:
ReplyDeleteprivate void WalkGraph(IVertex node, int level)
{
var componentModel = node as ComponentModel;
if (componentModel != null)
{
writer.WriteLine("{0}{1} -> {2}",
new string('\t', level),
componentModel.Service.FullName,
componentModel.Implementation.FullName);
}
foreach (var childNode in node.Adjacencies)
{
if(childNode != node)
WalkGraph(childNode, level + 1);
}
}