IMO, this new feature in Web Forms is not particularly well thought through. The main problem is that Web Forms breaks the IServiceProvider
contract.
The IServiceProvider.GetService
method defines that null
should be returned if no such service exists. But once you actually return null
, e.g. when you can’t construct that type, Web Forms throws a NullReferenceException
from deep down its stack.
Would Web Forms, on the other hand, have conformed to the IServiceProvider
abstraction, plugging in Simple Injector would have been a matter of a single statement, since SimpleInjector.Container
actually implements IServiceProvider
:
// WARNING: This won’t work
HttpRuntime.WebObjectActivator = container;
On top of this, when an IServiceProvider
is set through HttpRuntime.WebObjectActivator
, Web Forms will call it for almost everything, even for its own internal objects, which, to me, makes little sense.
Therefore, instead of supplying an IServiceProvider
implementation that is compatible to the IServiceProvider
contract, you will have to provide a special ASP.NET Web Forms-compatible IServiceProvider
implementation (which therefore breaks the contract).
Note that most DI Containers actually implement IServiceProvider
, but you would see most of them fail, because of this contract breach.
An adapter implementation would look like this:
class SimpleInjectorWebFormsServiceActivator : IServiceProvider
{
private const BindingFlags flag =
BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.CreateInstance;
private readonly Container container;
public SimpleInjectorWebFormsServiceActivator(Container container) =>
this.container = container;
public object GetService(Type serviceType) =>
serviceType.GetConstructors().Length > 0
? this.container.GetInstance(serviceType)
: Activator.CreateInstance(serviceType, flag, null, null, null);
}
And can be set as follows:
HttpRuntime.WebObjectActivator =
new SimpleInjectorWebFormsServiceActivator(container);
This implementation verifies whether the type contains public constructors and if so, it delegates the call to Simple Injector, which will construct the type. Otherwise, it will use Activator.CreateInstance
to construct the type.
Do note that using this implementation you don’t need custom IConstructorSelectionBehavior
, so you can remove your InternalConstructorResolutionBehavior
altogether.