Sunday, July 5, 2009

Castle: PerWebRequestLifeStyle won’t work from Application_Start

Update: Please read the follow up post on this topic.

I’m using the Castle project to implement dependency injection and inversion of control in my web applications.
Castle supports creating objects on a per-request basis which is a big help in keeping the number of objects created per request to a minimum.

I ran into a problem with this however. The PerWebRequestLifeStyle is implemented using an HttpModule to make sure objects are evicted at the end of a request. This works well except when using Castle from the Application_Start method (or the Init method for that matter).

The Application_Start method is invoked before any modules are initialized for the application and therefore any objects registered with Castle to use the PerWebRequestLifestyle cannot be used in Application_Start.

The problem manifests itself by reporting the module is probably not configured:

Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule
Add '<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.MicroKernel" />' to the <httpModules> section on your web.config.

Similar problems occur when performing tests outside the web environment because, well, there is no web request obviously.

Workaround
I’ve implemented a workaround by customizing the implementation of the lifestyle manager. This work-around falls back to the TransientLifeStyleManager in case the web context is not available. T
his enables both testing and application startup to use objects managed per web request.

[Serializable] 
public class PerWebRequestLifestyleManager : Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleManager 
{ 
  public PerWebRequestLifestyleManager() 
  { 
    _fallback = new TransientLifestyleManager(); 
  }

  public override object Resolve( CreationContext context ) 
  { 
    HttpContext current = HttpContext.Current;
    if ( null == current || current.ApplicationInstance == null )    
    {
      // fall back to transient behaviour if not in web context 
      return _fallback.Resolve( context ); 
    } 
    else 
    { 
      return base.Resolve( context ); 
    } 
  }

  public override void Dispose() 
  { 
    _fallback.Dispose(); 
    base.Dispose(); 
  }   
  
  public override void Init( IComponentActivator componentActivator, IKernel kernel, Castle.Core.ComponentModel model ) 
  { 
    base.Init( componentActivator, kernel, model ); 
    _fallback.Init( componentActivator, kernel, model ); 
  }   
  
  private TransientLifestyleManager _fallback; 
}

To make sure Castle uses this custom implementation I’m using a custom AttributeFacility:

internal class AttributeFacility : AbstractFacility
{
   protected override void Init()
   {
      Kernel.ComponentModelCreated += kernel_ComponentModelCreated;
   }

   void kernel_ComponentModelCreated( ComponentModel model )
   {
      var attributes = model.Implementation.GetCustomAttributes( typeof( Castle.Core.LifestyleAttribute ), false );
      if ( attributes.Length > 0 )
      {
         var attr = attributes[ 0 ] as LifestyleAttribute;
         if ( null != attr )
         {
            switch ( attr.Lifestyle )
            {
               case LifestyleType.PerWebRequest:
                  model.CustomLifestyle = typeof( Alanta.Services.PerWebRequestLifestyleManager );
                  model.LifestyleType = LifestyleType.Custom;
                  break;
               default:
                  break;
            }
         }
      }
   }    
}

I usually hook up the facility from code, like this:

var container = new WindsorContainer();
container.AddFacility<AttributeFacility>();

Update : this solution does not work on IIS7 in integrated pipeline mode. In this mode both HttpContext.Current and HttpContext.Current.ApplicationInstance are set so the detection method in the code above does not work.
I haven’t found a clean solution for this yet; I’ve had to resort to setting a flag in Application_Start that controls the behavior of the PerWebRequestLifestyleManager. Check out the demo code to see how this works.

Update : There is a discussion on this subject at the Castle Project Users group.

Update : Download the source code for this solution. The zip contains an ASP.NET MVC 1 demo application (VS 2008). The code in this

20 comments:

murki said...

Hey! Thanks for your great post, you're the only person I've seen addressing this problem, even though I have taken a different approach, do you know what would be the implications of using a Transient lifestyle instead of a PerWebRequest?

Marnix said...

Hi Murki,

A transient lifestyle would mean that every time you resolve a service a new instance is created. That may not be a problem in itself but needless creation of objects is a waste of server resources (mostly memory).
Also, I tend to use PerWebRequest lifestyle for things like a Linq DataContext. This ensures that every change to the database happens within the same context and all objects retrieved from the database are tracked by the same datacontext.
This would be impossible with a transient lifestyle.

murki said...

Hi Marnix,

Yeah, I'm basing some parts of my design on the suggestions of the book of "Pro ASP.NET MVC Framework" about using a PerWebRequest lifestyle in conjunction with Linq 2 Sql DataContext, but there the author contrast it against the default of "Singleton" which makes a lot of sense. Still I can't see how using a Transient lifestyle would make impossible to still use the DataContext, it would be an overkill maybe, but I don't think "impossible".

Marnix said...

Hi Murki,

I haven't read the book, but I think I agree with the author.

For simple applications where you have a single point of interaction with the datacontext in each request, transient lifestyle would do just fine. When things get more complicated problems will start to appear. For example, I use a filter to inject the current user into controller methods:

ActionResult DoStuff( [CurrentUser] User current ){
myRepo.DoStuffWithUser( current );
}

The filter retrieves the current user object from the database. In the action method the current user is passed to a repository to do some operations on it.
If the datacontext has a transient lifestyle the filter and the repository used here would each have their own instance of the datacontext. Using an object retrieved by one datacontext to perform database operations in another will cause problems because datacontexts track their objects. The second datacontext does not 'own' the object retrieved by the first datacontext and will either throw an exception or try to insert it (again!).

PerWebRequest lifestyle will prevent these problems because both the filter and the repository would get the same instance of the datacontext.

mausch said...

Hi Marnix, did you try asking on the castle users group? If this is bug it should be fixed.

Marnix said...

Hi Mausch,

Good point. I haven't had a chance to report the issue so I've gone and done it right away.

Thanks,
Marnix

ploeh said...

Regarding getting PerWebRequest to work on IIS7, see my blog post Using Castle Windsor's PerWebRequest lifestyle with ASP.NET MVC on IIS7.

bhoomi said...

I am still having the same problem. Tried various things. Implemented your code also. Tried out ploeh's way also. Nothing seems to work for me. I am using Castle.Windsor 2.0.0.0.
I am running my solution from VS Integrated environment.

Pl help.

Marnix said...

Hi Bhoomi,

What version of Visual Studio are you using?

bhoomi said...

Visual Studio 2008.

Marnix said...

Hi Bhoomi,

I've created a basic demo application that demonstrates my solution and added a link to the blog post.
Please try the application and let me know if this works for you.

bhoomi said...

Hi Murki,

Thanks for the code. Yes your code works correctly.
I replicated your Web.Config file but still I am getting the same error that is :

Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule
Add '' to the section on your web.config


It comes from :
ServiceLocator.Current.GetInstance(commandHandler);

here commandHandler is a generic of type : ICommandHandler

Any ideas wht's wrong??

Thanks again..

Marnix said...
This comment has been removed by the author.
Marnix said...

Hi Bhoomi,

I've updated the demo code to make it a bit simpler to use. Please download the code again and see if that works for your application.

David Aleu said...

Anything new about this issue? I'm using Castle 2.5 and in my project your solution doesn't solve my problem (I can access the container but not the registered components).

Marnix said...

As far as I know the position of the Castle team is that the behavior of the PerWebRequest lifestyle is by design. They are not planning to fix or change it.

mausch said...

A while ago I created a contrib project with several lifestyles not included in stock Windsor (hybrid transient-web included).

Marnix said...

Looks good Mauricio. Especially since it also has the contextual life style you commented about on my Stack Overflow question a while back.

Fabian Fernandez said...

Hi Marnix!
Thanks a lot for this post!
As I was trying to implement your proposed workaround I noted that Castle 3 has removed the PerWebRequestLifeStyleManager, I searched a little and found this.
It would be nice a workaround version for Castle 3.
Thanks again!

Fabian Fernandez said...

Marnix, here's my version of PerWebRequestLifestyleManager refactored for Castle 3:

[Serializable]
public class PerWebRequestLifestyleManager : ScopedLifestyleManager
{
private TransientLifestyleManager fallback;

public PerWebRequestLifestyleManager()
: base(new WebRequestScopeAccessor())
{
fallback = new TransientLifestyleManager();
}

public override object Resolve(CreationContext context, IReleasePolicy releasePolicy)
{
HttpContext current = HttpContext.Current;

if (null == current || current.ApplicationInstance == null)
{
// Fall back to transient behavior if not in web context.
return fallback.Resolve(context, releasePolicy);
}
else
{
return base.Resolve(context, releasePolicy);
}
}

public override void Dispose()
{
fallback.Dispose();

base.Dispose();
}

public override void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model)
{
base.Init(componentActivator, kernel, model);

fallback.Init(componentActivator, kernel, model);
}
}