Using ASP.Net Webform Dependency Injection with .NET 4.7.2

Standard

ASP.Net logoStarting 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.

Wikipedia

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();

Plugging Autofac

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 ContainerBuilder:

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 Items property).
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.

Final word

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 :