' 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. This 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
Fabian Fernandez
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);
}
}
Fabian Fernandez
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!
Marnix
Looks good Mauricio. Especially since it also has the contextual life style you commented about on my Stack Overflow question a while back.
Mauricio Scheffer
A while ago I created a contrib project with several lifestyles not included in stock Windsor (hybrid transient-web included).
Marnix
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.
David Aleu
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
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.
Marnix
This comment has been removed by the author.
Bhoomi
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
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
Visual Studio 2008.
Marnix
Hi Bhoomi,
What version of Visual Studio are you using?
Bhoomi
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.
ploeh
Regarding getting PerWebRequest to work on IIS7, see my blog post Using Castle Windsor's PerWebRequest lifestyle with ASP.NET MVC on IIS7.
Marnix
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
Mauricio Scheffer
Hi Marnix, did you try asking on the castle users group? If this is bug it should be fixed.
Marnix
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.
Unknown
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
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.
Unknown
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?