Starting with .NET 4.7.2 (released April, 30st), Microsoft offers an endpoint to plug our favorite dependency injection container when developing ASP.Net Webforms applications, making possible to inject dependencies into UserControls, Pages and MasterPages.
In this article we are going to see how to build an adaptation layer to plug Autofac or the container used in ASP.Net Core.
Dependency Injection for Webforms
In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
In our example we would like to inject a dependency decoupled with the
IDependency interface, into our Index page and our Master page.
According to Microsoft, in the release note of the framework, the extension point is by implementing
IServiceProvider and using it in the
Init method of the
Global.asax this way :
HttpRuntime.WebObjectActivator = new MyProvider();
When building an Autofac container, we end up with an object implementing the
IContainer. So, we have to build an adapter that wraps the Autofac container and forwards
The first version is quite straightforward, we call Autofac if the type is registered else we rely on the
Activator class :
However, it won’t work well. In fact, Webform subclass the Webform objects (Pages, UserControls, MasterPage) at runtime making them impossible to register in the
ContainerBuilder. Therefore, for all those objects we are going to end up in the case with the Activator.
Hopefully, Autofac provides a way to dynamically declare registrations using the concept of
RegistrationSource. By implementing one, we can then register at runtime our Webforms objects.
Subclassed Webforms objects are by default declared in the
ASP namespace, if we are asked a type in this namespace, we generate a registration else we let it go through.
Once we have this RegistrationSource, we can use it in our
Please note that in this case, we never register the Index page or Master page.
Allowing “per request” lifetime
It would be interesting to make the “per request” lifetime available. This way, all the objects of the request (the page, the handler, the master page, etc.) can share the same instance of the dependencies. It is a kind of singleton but only per HTTP request, making it safe to use (unlike a simple singleton).
To provide this, Autofac usually creates a
LifetimeScope, uses it and stores it in a per request bag (located in the current
HttpContext, in the
We are going to do the same in our
AutofacServiceProvider : try to retrieve an existing instance of the
LifetimeScope, creating it and storing it if needed and when the requests end, disposing it. If there is no HttpContext, we end up with the root scope, the container itself.
Now, when registering a dependency with
InstancePerRequest() method, it makes only one instance per HTTP request.
Using Microsoft Dependency Injection container
We can use the same technique to use the container from Microsoft. Although the container instance implements the IServiceProvider, we have to wrap it anyway. In fact, we need to do this to handle the “per request” scope.
The main difference with Autofac is that there’s no
RegistrationSource as this concept only exists in Autofac. However, there is a helper method
ActivatorUtilities.GetServiceOrCreateInstance which allows to create an instance of an unregistered component passing registered dependencies to the constructor. Therefore, we can use this to create our instances.
We’ve seen how to create wrappers around famous dependency injection containers to provide dependency injection for ASP.Net Webforms thanks to the new extension point available from .Net 4.7.2.
It is now possible to make clean dependency injection in Pages and prepare our legacy apps to transition to ASP.Net Core.
You can find the full samples on my GitHub :