Julian Jelfs’ Blog

Detecting possible lifestyle conflicts in the Windsor container

Posted in Castle by julianjelfs on April 28, 2009

When using the Castle Windsor container you have to make decisions about the lifestyles of your components. The common options are Singleton (which is the default) and Transient. If you are using the container within a web application you are likely to also want to use the PerWebRequest lifestyle.

When you have a complex object graph with lots of dependencies problems can arise if you have inadvertently given one or more components the wrong lifestyle. Generally speaking it is most likely to cause problems if a component has a dependent component which has a shorter lifestyle than the component it is a dependent of.

Let’s think of a concrete example using the following two classes:

public class UserContext
{
    public int SessionId {get;set;}
}

public class DoStuffService
{
    public DoStuffService(UserContext context)
    {
        ....
    }
}

In this case the UserContext class contains some information that pertains to the context in which the code is executing, namely the session id. For this reason we rightly want use the PerWebRequest lifestyle for this component (I’m not going to worry about how this component gets it’s SessionId property populated – that’s not the point).

Now, what is less obvious is that this decision implies that the DoStuffService must also have a lifestyle with a duration less than or equal to UserContext. Consider the following container configuration:

new WindsorContainer()
    .Register(
         Component.For<UserContext>().LifeStyle.PerWebRequest,
         Component.For<DoStuffService>()
        );

This is perfectly valid and you will get no complaints from the container, but what it means is that only one instance of the DoStuffService will be created by the container since the lifestyle defaults to Singleton. When it is constructed for the first and only time, we will get an instance of the UserContext injected in correctly. However, the next web request will expect a new instance of UserContext but it will not get one. i.e. UserContext has effectively become a Singleton as well.

If you are lucky, your code will fail. But the really nasty thing is that in a single user environment e.g. a development environment, Singleton and PerWebRequest can behave in exactly the same way so that you never notice a problem. Furthermore, the problem is unlikely to be as obvious as it is in this example with a much more complicated object graph.

You can see that this is not really something that the container can prevent in any generic way because there is no real way to tell whether it will in practice cause a problem. However, it is possible for us as users to decide on a policy and then to validate that the configured container conforms to that policy. A good policy seems to be that a component cannot have a lifestyle that is longer in duration than any of  it’s dependent components. Such a policy can be implemented as follows:

public static void ValidateLifestyles(this IWindsorContainer container)
{
    var errors = new ContainerLifeStyleException();
    var comparer = new LifeStyleComparer();

    foreach (var node in container.Kernel.GraphNodes)
    {
        var component = (ComponentModel)node;
        foreach (var dependent in node.Dependents)
        {
            var dependentComponent = (ComponentModel)dependent;
            if (comparer.Compare(component.LifestyleType, dependentComponent.LifestyleType) > 0)
                errors.Add(component, dependentComponent);
        }
    }

    if (errors.ContainsErrors)
        throw errors;
}

ContainerLifeStyleException is just a collection of all of the problems found along the way and LifeStyleComparer is just an implementation of IComparer<LifestyleType> so that you can decide which lifestyles are of longer duration than others .

You can then call this extension method immediately after configuring your container and any potential lifestyle problems will be reported.

Advertisements
Tagged with:

2 Responses

Subscribe to comments with RSS.

  1. Richard Dingwall said, on May 11, 2010 at 11:00 am

    Annoyed that Windsor doesn’t do this out of the box.

    Here’s my complete implementation of this validation including the WindsorLifestyleConflictException and WindsorLifestyleComparer:

    http://richarddingwall.name/wp-content/uploads/2010/05/WindsorExtensions.cs

    Cheers, Rich

    • julianjelfs said, on May 11, 2010 at 11:08 am

      Good stuff – thanks for sharing!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: