views:

216

answers:

2

We have a number of castle windsor components declared in a config file. Some of the components somewhere deep inside might require the services of other components.

The problem is when the application is being closed and the Container is being disposed. During Dispose()/Stop() of the Startable/Disposable component (A) when it requires the services of some other component (B) ComponentNotFoundException then raised. By that time B is already removed from the container.

I've noticed that the order of components declarations in app config file is important. And reodering A and B solves the problem.

Is there a better way to influence the order in which the components are disposed?

Edited: Following a request in comments I provide here a sample code that will throw ComponentNotFoundException:

class Program
{
    static void Main()
    {
        IoC.Resolve<ICriticalService>().DoStuff();
        IoC.Resolve<IEmailService>().SendEmail("Blah");
        IoC.Clear();
    }
}

internal class CriticalService : ICriticalService, IStartable
{
    public void Start()
    {}

    public void Stop()
    {
        // Should throw ComponentNotFoundException, as EmailService is already disposed and removed from the container
        IoC.Resolve<IEmailService>().SendEmail("Stopping");
    }

    public void DoStuff()
    {}
}

internal class EmailService : IEmailService
{
    public void SendEmail(string message)
    {
        Console.WriteLine(message);
    }

    public void Dispose()
    {
        Console.WriteLine("EmailService Disposed.");
        GC.SuppressFinalize(this);
    }
}

internal interface ICriticalService
{
    void DoStuff();
}

internal interface IEmailService : IDisposable
{
    void SendEmail(string message);
}

public static class IoC
{
    private static readonly IWindsorContainer _container = new WindsorContainer(new XmlInterpreter());

    static IoC()
    {
        _container.AddFacility<StartableFacility>();
        // Swapping the following 2 lines resolves the problem
        _container.AddComponent<ICriticalService, CriticalService>();
        _container.AddComponent<IEmailService, EmailService>();
    }

    public static void Clear()
    {
        _container.Dispose();
    }

    public static T Resolve<T>()
    {
        return (T)_container[typeof(T)];
    }
}

Note: See a comment in the code how swapping the order of inserting components in the container solves the problem.

A: 

I, personally, feel that any system that requires Dispose() to be called in a specific order has a flaw in the design.

Dispose() should always be safe to call. The errors should only occur if a component is used after disposal, and then ObjectDisposedException makes the most sense. In a case like this, I would rework your components so that they don't use other componetry during their Dispose() method (it really should be about cleaning each component's own, private resources). This would eliminate this issue entirely.

Reed Copsey
On one hand you are right, and I should probably iterate through all Startable components and call Stop() on them in the order I like, and then call Castle.Windsor.IWindsorContainer.Dispose(). On the other hand, I would expect the Castle framework to do it on my behalf as they provide Dispose() method.
Boris Lipschitz
"One of unique features of Windsor is that it manages the lifecycle of objects it creates for you. What this means (among other things) is that it will dispose all disposable objects it instantiates."
Boris Lipschitz
That really has nothing to do with it, though. They'll call Dispose on your components, but your components should not rely on other components to exist once Dispose() has been called. The order in which disposal occurs shouldn't matter...
Reed Copsey
+2  A: 

By having a static IoC class you're actually using the container as a service locator, thus losing most of the benefits of dependency injection.

The problem is that without a proper injection, Windsor doesn't know about the CriticalService - IEmailService dependency, so it can't ensure the proper order of disposal.

If you refactor to make this dependency explicit, Windsor disposes the components in the correct order:

internal class CriticalService : ICriticalService, IStartable
{
    private readonly IEmailService email;

    public CriticalService(IEmailService email) {
        this.email = email;
    }
 ...
}

Here's how it would look like after refactoring.

Mauricio Scheffer
Thanks for your help. I completely agree with your comments about constructor dependency. However, bear in mind that it was just a sample, our system is much bigger and more complicated. It's not easy to achieve, but we will try to refactor this area. As for StartableFacility, I just forgot to add it to the sample code (left it in config file). Please see the updated sample code. It doesn't work with startable facility.
Boris Lipschitz
Tested with 2.1.1 and StartableFacility - ComponentNotFoundException
Boris Lipschitz
By the way, what's your opinion on this pattern by Ayende:http://ayende.com/Blog/archive/2006/07/30/IoCAndFluentInterfaces.aspx
Boris Lipschitz
Boris, about the post by Ayende - it is a bad thing to do. We now have a much better tools than ServiceLocator, namely TypedFactoryFacility, DynamicParameters, UsingFactoryMethod... strive to **not** to have to call to the container in your code, outside of single well defined place in your inftastructure (like controller factory)
Krzysztof Koźmic
Mauricio Scheffer
@Mauricio: I tried your code. It works fine. I've sent you my sample project to your gmail account. Thanks!
Boris Lipschitz
@Krzysztof: Thanks a lot for the info. We developed our framework about 2 years ago, based on Ayende's blog posts about Castle and RhinoMocks. Maybe it's about time to review it...
Boris Lipschitz