RegisterPerWebRequest() and container.Verify()

Developer
Oct 11, 2013 at 12:13 PM
Edited Oct 11, 2013 at 12:24 PM
In an ASP.net MVC web application I would like to pass the current user to my controllers via constructor injection. I register my custom IWebUser interface via the following factory-method:
container.RegisterPerWebRequest<IWebUser>(() => HttpContext.Current != null ? HttpContext.Current.User as IWebUser : null);
But this throws an exception in the Verify() call on the container:
"The registered delegate for type XyzController threw an exception. The registered delegate for type IWebUser returned null."
Is container verification not supported with WebRequestLifestyle registrations in the container? Am I supposed to return a mock objects in all PerWebRequest<> registrations for the container verification - e.g. when HttpContext.Current == null?

In most situations I guess I could just use transient registrations and mock objects (as in this case). I have not directly found any Information about this in the documentation and I think this might be a quite common issue for everybody using RegisterPerWebRequest() together with Verify() ... maybe I am missing something?
Developer
Oct 11, 2013 at 1:03 PM
I just noticed that in all practical situations (even during Global.asax.cs Application_Start()) there is a HttpContext.Current object. So my case is a rather special one. I will either make sure that the User property is always of type IWebUser or fall back to a special anonymous user impl.

In General RegisterPerWebRequest() and container.Verify() should not be a problem. I was just wrongly assuming that HttpContext.Current being null during my Verify() call in Global.asay.cs .. which is not the case. sorry.
Coordinator
Oct 11, 2013 at 10:07 PM
Is container verification not supported with WebRequestLifestyle registrations in the container?
Verifying the container with the WebRequestLifestyle is absolutely supported. Simple Injector however does not permit registered factory delegates to return null. It ever wraps your delegate with a special guard delegate to prevent null from ever being injected into any constructor.
Am I supposed to return a mock objects in all PerWebRequest<> registrations for the container verification
To allow your container to keep verifiable, you will sometimes have to do something extra. A solution could be returning a mock, or perhaps only return a mock when the container is verifying (there's a IsVerifying() extension method to check this in the SimpleInjector.Advanced namespace). I used to register my user in my own applications as follows:
container.RegisterPerWebRequest<IPrincipal>(() => HttpContext.Current.User ?? new GenericPrincipal(new GenericIdentity(), null));
But later I found out that the Thread.CurrentPrincipal always has a value, so this is much easier:
container.RegisterPerWebRequest<IPrincipal>(() => Thread.CurrentPrincipal);
Marked as answer by dot_NET_Junkie on 3/2/2014 at 11:12 AM
Developer
Oct 12, 2013 at 2:36 PM
Nice idea to register an IPrincipal!

btw: Is there already a buil-in web-request/transient hybrid life-style?
var hybridLifestyle = Lifestyle.CreateHybrid(
    lifestyleSelector: () => HttpContext.Current != null,
    trueLifestyle: new SimpleInjector.Integration.Web.WebRequestLifestyle(),
    falseLifestyle: Lifestyle.Transient
);
I read that this once was the default behavior of the WebRequestLifestyle but was replaced by the policy only to create objects inside web requests (which is good by default). Nevertheless I think there are some situations in which a hybrid lifestyle is sensible and It would be great to have this alternative lifestyle as part of the SimpleInjetor.Integration.Web assembly.
Coordinator
Oct 13, 2013 at 9:32 AM
It's important for the framework to allow creating hybrid lifestyles and this was an important shortcoming of the v1.x releases. The 'built-in' way to use hybrid lifestyles in v2 however, is by using the Lifestyle.CreateHybrid method. Since creating hybrid lifestyles (like the one shown in your last reply) is really easy using Lifestyle.CreateHybrid (it's practically a one-liner), there's no need to add alternative lifestyle implementations to the framework. Adding a new lifestyles, such as a HybridWebRequestTransientLifestyle, HybridWebRequestLifetimeScopeLifestyle and HybridWebRequestSingletonLifestyle would just clutter the API, making it bigger and harder to choose.

And especially making a hybrid using the Web Request and Transient lifestyles is an unlikely combination and would therefore be a bad fit as standard lifestyle in the framework. The reason that mixing web request with transient is unlikely is because the Web Request is a scoped lifestyle. Scoped lifestyles are used ensure that a single instance is returned within a certain scope (usually some sort of request). You would usually want those instances to alwats be singletons within a request, no matter whether you run them in a web request or on a background thread.

Take for instance a unit of work (such as Entity Framework's DbContext or ObjectContext classes). Those are your typical candidates to be registered using a scoped lifestyle. If you start a new background thread for processing, you still want the same DbContext to be injected throughout the complete object graph, since injecting a transient unit of work would usually break the application.

So I would say that in most cases wanting to mix a scoped lifestyle with a non-scoped lifestyle should raise a red flag. This doesn't mean that a Web Request-Transient hybrid is always bad, but it's just unlikely enough to exclude it from the framework.
Developer
Oct 13, 2013 at 11:32 PM
Edited Oct 14, 2013 at 12:31 AM
OK Steve, you are probably right that it is not a common use case. I use the hybrid web-request/transient registration mainly as a performance optimization for some objects which are costly to initialize and which would be constructed multiple times without the scope registration during a web-call. Of course the most important use-case for scoping should be objects which require deterministic destruction (implement IDisposable) and for those it is never acceptable to create them as untracked transient objects.

After your 'red flag' words I will now once again think about whether it is wise to miss-use the request-scope as an instance/cache performance tweak. ;)