butler
OOD / OOP

Transient objects with IoC containers like Castle Windsor

butler

Dependency Injection (DI) and Inversion of Control (IoC) are popular patterns in modern software development that reduce coupling among modules and so improve the testability of the sames. As this article is not meant to be an introduction to DI / IoC, please refer to numerous introductory articles on the net before tackling this topic, as we will delve straight into the implementation’s details. For these samples, we will use Castle Windsor as IoC container, and we will build the same sample three times, highlighting the differences in handling permanent and transient class instances with an IoC container. The aim is creating an implementation that has minimal coupling and is highly testable. 

Let’s start by drawing the scenario: our simple app uses the IoC container for cross-cutting concerns, in this example only logging but in a real app there could be a lot more functionalities of this kind. e.g. data model access, telemetry and so on. The logging framework is a singleton that spans the whole life of the app, so once we register the interface in the IoC container, and let the container inject the instance into the objects that use it, we are golden. But temporary objects, like a child window, are created and then destroyed when needed, so the same instance cannot span the whole life of the app. At the same time, these temporary objects need to access the functionalities such as logging, so those instances must be injected into the freshly created object.

Let’s start with the first implementation to see some real code (before proceeding, download the whole solution from GitHub). The project ManualIoC contains a poor man’s implementation of DI, as we have to manually pass the instances to the transient objects. First, let’s start with the main program, where we initialize the IoC container:

static void Main()         
{             
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);       // create IoC container     IWindsorContainer container = CastleContainer.Instance;                 container.Register(Component.For<ILogger().ImplementedBy<ConsoleLogger>());
    container.Register(Component.For<MainForm>());                   //Application.Run(new MainForm());     MainForm mainForm = container.Resolve<MainForm>();     Application.Run(mainForm);               
    container.Dispose();                     
}

There are several changes compared to a stock WinForms application: before creating the main form of the app, we create the IoC container, and we register the implementation of the ILogger service, and the MainForm class. Instead of creating directly the MainForm like it happens with the standard WinForms code, we ask the IoC container to resolve, that is returning an instance of the given class, the MainForm and then we run the application. The critical bit is that MainForm requires an ILogger instance in the constructor, and the IoC container knows about it, so it transparently creates a singleton for the ILogger interface using the ConsoleLogger implementation and passes it to the constructor of MainForm, without requiring any intervention from our side. With a simple demo it is hard to understand how an IoC controller can simplify a real project, but assume that there is not just one cross-cutting functionality like logging in the example, but many more and you will understand how the code gets clearer and simpler. Also, rewiring the app to use a different logging framework requires just creating a new implementation of the ILogger interface that uses the new framework, and changing just the registration of the ILogger interface in this bit of code.

Now let’s have a look at the CastleContainerClass that hosts the IoC container:

public static class CastleContainer {     private static IWindsorContainer container;       public static IWindsorContainer Instance     {         get         {             if (container == null)             {                 container = new WindsorContainer();             }             return container;         }         // exposing a setter alleviates some common component testing problems         set { container = value; }     }       public static T Resolve<T>()     {         return Instance.Resolve<T>();     }       public static void Dispose()     {         if (container != null)             container.Dispose();         container = null;     } }

It is just a singleton pattern for hosting a single instance of the container for the whole execution of the application.

For the sake of completeness, let’s just have a look at the ILogger interface and the ConsoleLogger implementation:

public interface ILogger {     
   void Error(string logMessage);     
   void Warning(string logMessage);    
   void Info(string logMessage);     
   void Debug(string logMessage); 
}
public class ConsoleLogger : ILogger {     public void Error(string logMessage)     {         Console.WriteLine("ERROR: " + logMessage);     }       public void Warning(string logMessage)     {         Console.WriteLine("WARN:  " + logMessage);     }       public void Info(string logMessage)     {         Console.WriteLine("INFO:  " + logMessage);     }       public void Debug(string logMessage)     {         Console.WriteLine("DEBUG: " + logMessage);     } }

Nothing to see here, let’s move along. In the main window, let’s a button click handler that opens a child window:

public partial class MainForm : Form {     
    private ILogger Logger;     
    public MainForm(ILogger _Logger) {         
        InitializeComponent();           
        Logger = _Logger;     
    }

    private void openButton_Click(object sender, EventArgs e) {
        Logger.Info("Creating new child window");         ChildForm newChildForm = new ChildForm(Logger);         newChildForm.ShowDialog();         Logger.Info("Returned from child window");     
    } 
}

The event handler is using the ILogger instance passed to the constructor and saved in a private field for further reference. In this first example, the child window is created manually, so we have to pass the instance of the logger to the constructor:

ChildForm newChildForm = new ChildForm(Logger); 

The child form receives the ILogger in the constructor, stores it in a private field, and uses it to log the closing of the window:

public partial class ChildForm : Form {     
    ILogger Logger;       
    public ChildForm(ILogger _Logger) {         
        InitializeComponent();           
        Logger = _Logger;     
    }

    private void closeButton_Click(object sender, EventArgs e) {         
        Logger.Info("Closing window");         
        Close();     
    } 
}

Now that we have understood what we want to achieve, let’s start using the IoC container also for the child form. The IoCResolve project marks the first step in this direction. First, we need to register the ChildForm class in the container:

static void Main() {     
    Application.EnableVisualStyles();     Application.SetCompatibleTextRenderingDefault(false);       // create IoC container     IWindsorContainer container = CastleContainer.Instance;                 container.Register(Component.For<ILogger>().ImplementedBy<ConsoleLogger>());                 
    container.Register(Component.For<MainForm>());     container.Register(Component.For<ChildForm>().LifeStyle.Transient);       //Application.Run(new MainForm());     MainForm mainForm = container.Resolve<MainForm>();     Application.Run(mainForm);       container.Dispose();             
}

This line:

container.Register(Component.For<ChildForm>().LifeStyle.Transient); 

specifies that the ChildForm class should not be treated like a singleton, with a single instance used for the whole life of the app, but a new instance should be created every time we require the container to resolve the class.

Now that the class is registered in the IoC container, let’s resolve it instead of manually creating it:

private void openDialogButton_Click(object sender, EventArgs e) {     Logger.Info("Creating new child dialog window");     ChildForm newChildForm = CastleContainer.Instance.Resolve<ChildForm>();     newChildForm.ShowDialog();     Logger.Info("Returned from child dialog window");     CastleContainer.Instance.Release(newChildForm); 
}

Resolving the ChildForm means creating a new instance of the class and passing the ILogger instance to the constructor. It is very important to remember that Castle Windsor tracks the instances created by Resolve, so they will not be deleted by the GC (garbage collector) when they are not in use anymore. For this reason, after using the newly create instance, don’t forget to pass it to Release, or you will have a big memory leak.

CastleContainer.Instance.Release(newChildForm); 

In this sample the child form is shown modally, so the button click handler waits for the child form to be closed before continuing. What if the form is non-modal? As stated before, we have to tell the IoC container to release the instance of the form or memory will leak. Adding a delegate that takes care of this to the Closed event of the child form fixes the problem:

private void openButton_Click(object sender, EventArgs e) {     Logger.Info("Creating new child window");     ChildForm newChildForm = CastleContainer.Instance.Resolve<ChildForm>();     newChildForm.Closed += delegate {
        Logger.Info("Closed child window");         CastleContainer.Instance.Release(newChildForm);     
    };     
    newChildForm.Show();     Logger.Info("Returned from child window");                     }

To verify that we are actually creating and detroying the form instances at the right time, let’s log these events:

public MainForm(ILogger _Logger) {     
    InitializeComponent();       
    Logger = _Logger;       CastleContainer.Instance.Kernel.ComponentCreated += Kernel_ComponentCreated;     CastleContainer.Instance.Kernel.ComponentDestroyed += Kernel_ComponentDestroyed; 
}   

/// <summary> /// log when a new component is create in the container /// </summary> /// <param name="model"></param> /// <param name="instance"></param> void Kernel_ComponentCreated(Castle.Core.ComponentModel model, object instance) {     Logger.Debug("Component created: " + model.Name); 
}   

/// <summary> /// log when a component resolved by the contained is destroyed /// </summary> /// <param name="model"></param> /// <param name="instance"></param> void Kernel_ComponentDestroyed(Castle.Core.ComponentModel model, object instance) {     
    Logger.Debug("Component destroyed: " + model.Name); 
}

Now if you run the demo app inside Visual Studio, in the output window you can verify that the form instances are correctly created and destroyed.

Summing up, with this implementation we have made a step forward, as now we don’t have to track all the services used by child forms and the IoC container will take care of feeding them the right instances. At the same time, from the point of view of coupling and testability, we have the code of the main form with direct references to the IoC container, and this makes testing more complex than it should. Besides, ideally we should avoid referring to the IoC container after initialization, otherwise we are using the IoC container more like a repository of instances.

Let’s make one step forward with a new project named IoCFactory.

static void Main() {     
    Application.EnableVisualStyles();     Application.SetCompatibleTextRenderingDefault(false);       // create IoC container     IWindsorContainer container = CastleContainer.Instance;     container.AddFacility<TypedFactoryFacility>();     container.Register(Component.For<IChildFormFactory>().AsFactory());     container.Register(Component.For<ILogger>().ImplementedBy<ConsoleLogger>());                 
    container.Register(Component.For<MainForm>());     container.Register(Component.For<ChildForm>().LifeStyle.Transient);       //Application.Run(new MainForm());     MainForm mainForm = container.Resolve<MainForm>();     Application.Run(mainForm);       
    container.Dispose();             
}

First, we add to the container the Typed Factory Facility:

container.AddFacility<TypedFactoryFacility>(); 

Then we register a new interface as a factory:

container.Register(Component.For<IChildFormFactory>().AsFactory()); 

Now for the cool part: defining a factory for the ChildForm class requires only the interface definition, no implementation is necessary, as long as stick to the naming conventions and let Castle Windsor take care of the plumbing:

public interface IChildFormFactory {     
    ChildForm Create();             
    void Release(ChildForm _form); 
}

Now we inject the factory into the constructor of the main form:

private ILogger Logger; private IChildFormFactory ChildFormFactory;   public MainForm(ILogger _Logger, IChildFormFactory _ChildFormFactory) {     InitializeComponent();       
    Logger = _Logger;     
    ChildFormFactory = _ChildFormFactory;       CastleContainer.Instance.Kernel.ComponentCreated += Kernel_ComponentCreated;     CastleContainer.Instance.Kernel.ComponentDestroyed += Kernel_ComponentDestroyed; 
}

When it comes to creating the child form, we use the factory instead of calling Resolve in the container:

private void openDialogButton_Click(object sender, EventArgs e) {     Logger.Info("Creating new child dialog window");     ChildForm newChildForm = ChildFormFactory.Create();     newChildForm.ShowDialog();     Logger.Info("Returned from child dialog window");     ChildFormFactory.Release(newChildForm); 
}

Similarly, we change the code that shows a non-modal window to refer to the factory:

private void openButton_Click(object sender, EventArgs e) {     Logger.Info("Creating new child window");     ChildForm newChildForm = ChildFormFactory.Create();     newChildForm.Closed += delegate     {         Logger.Info("Closed child window");         ChildFormFactory.Release(newChildForm);                    
    };     
    newChildForm.Show();     Logger.Info("Returned from child window");                     
}

Mission accomplished! No references to the IoC container in the code, we can fake / mock the child form factory for testing.

Leave a Reply

Your email address will not be published. Required fields are marked *