Monday, March 03, 2008

How to output Windsor Container's dependency graph

I was asked about this today (hi Sham!). It turns out to be very easy since the Windsor Container exposes it's dependency graph via it's GraphNodes collection. Here's a little console application that'll output the dependency graph of any application. You simply give it the path of the Windsor configuration file and the path of your application's bin folder.

Here's the output from a sample run:

C:\>Suteki.DependencyViewer
"C:\source\Suteki\Suteki.SomeApplication\Configuration\Windsor.config"
"C:\source\Suteki\Suteki.SomeApplication\bin"

Suteki.SomeApplication.Controllers.StatusController
        Suteki.SomeApplication.Repositories.UserRepository
                Suteki.SomeApplication.SomeApplicationDataContext
        Suteki.SomeApplication.Repositories.InstructionRepository
                Suteki.SomeApplication.SomeApplicationDataContext
        Suteki.SomeApplication.Repositories.Repository`1
                Suteki.SomeApplication.SomeApplicationDataContext
        Suteki.SomeApplication.Repositories.Repository`1
                Suteki.SomeApplication.SomeApplicationDataContext
        Suteki.SomeApplication.Services.MatterSubmissionService
                Suteki.SomeApplication.MatterServiceProxy.MatterServiceClient
                Suteki.SomeApplication.Services.DocumentUploadServiceProxy
                Suteki.SomeApplication.Services.InstructionPdfService
                        Suteki.SomeApplication.Service.Model.Services.DocumentService
                                Suteki.SomeApplication.Service.Model.Services.DateService
        Suteki.SomeApplication.Services.EmailToFileSender
        Suteki.SomeApplication.Services.EmailTemplateService

And here's the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.Windsor;
using Castle.Core;
using System.Reflection;
using System.IO;

namespace Suteki.DependencyViewer
{
    public class Program
    {
        private Dictionary<string, Assembly> assemblyList;

        public static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("usage: Suteki.DependencyViewer <config file> <bin directory>");
                return;
            }

            Program program = new Program();
            program.Execute(args[0], args[1]);
        }

        private void Execute(string configPath, string binPath)
        {
            if (!File.Exists(configPath))
            {
                Console.WriteLine("The config file at '{0}' does not exist");
            }
            if (!Directory.Exists(binPath))
            {
                Console.WriteLine("The bin directory at '{0}' does not exist");
            }

            LoadDlls(binPath);

            WindsorContainer container = new WindsorContainer(configPath);

            GraphNode[] graphNodes = container.Kernel.GraphNodes;

            foreach (GraphNode graphNode in graphNodes)
            {
                if (graphNode.Dependers.Length == 0)
                {
                    Console.WriteLine();
                    WalkGraph(graphNode, 0);
                }
            }
        }

        private void WalkGraph(IVertex node, int level)
        {
            ComponentModel componentModel = node as ComponentModel;
            if (componentModel != null)
            {
                Console.WriteLine("{0}{1}", new string('\t', level), componentModel.Implementation.FullName);
            }

            foreach (IVertex childNode in node.Adjacencies)
            {
                WalkGraph(childNode, level + 1);
            }
        }

        private void LoadDlls(string binPath)
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            string[] dlls = System.IO.Directory.GetFiles(binPath, "*.dll");
            assemblyList = new Dictionary<string, Assembly>(dlls.Length);

            foreach (String fileName in dlls)
            {
                Assembly asm = System.Reflection.Assembly.LoadFrom(fileName);
                assemblyList.Add(asm.FullName.Split(',')[0], asm);
            }
        }

        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return assemblyList[args.Name];
        }
    }
}

3 comments:

Anonymous said...

The title said something about graph, so where is the graph ?

Mike Hadlow said...

Anonymous, very good ;)

fschwiet said...

Thanks for thinking to post this.