20
votes

I am in the process of introducing a Dependency Injection framework into an existing WebForms application (using Castle Windsor).

I have pretty deep experience with DI, and tend to very strongly favor constructor injection over setter injection. If you are familiar with Webforms, you know that the ASP.Net framework handles the construction of page and control objects, making true constructor injection impossible.

My current solution is to register the container in the Application_Start event of the Global.asax, and keep the container as a public static variable in Global as well. I then simply resolve each service that I need directly in the page or control when I need them. So at the top of each page, I end up with code like this:

private readonly IMyService _exposureManager = Global.IoC.Resolve<IMyService>();
private readonly IMyOtherService _tenCustomersExposureManager = Global.IoC.Resolve<IMyOtherService>();

Obviously, I don't like having all these references to the container scattered about my application or having my page/control dependencies be non-explicit, but I have not been able to find a better way.

Is there a more elegant solution for using DI with Webforms?

6
I found this article codemag.com/Article/1210031 which has excellent sample code as to how to implement dependency injection on WPF, MVC and WebForms (using PageHandlerFactory, in the case of WebForms). It seems to say that because of code-behind class limitations, you will have to use setter injection. It also, interestingly, shows how Microsoft Managed Extensibility Framework (MEF) can help you to solve this and similar DI problems in a very useful and slightly non-standard way.MikeBeaton
Starting .NET 4.7.2 there is a Dependency Injection in WebForms. See my answer bellow.t00thy

6 Answers

19
votes

I agree with @DarinDimitrov that MVP is an interesting option. However, when working with a legacy application, rewriting an existing page to the MVP pattern is a hell of a job. In that case it might be better to start with the Service Locator pattern (but only in your UI classes) as you are already doing. However, do change one thing. Do not expose the chosen DI container to the application, as I expect you are doing with the Global.IoC property.

Instead, create a static Resolve<T> method on the Global class. This hides the container completely and allows you to swap implementations without having to change anything in your web pages. When you do this, there is no advantage in using the Common Service Locator as @Wiktor proposes. The Common Service Locator is just another abstraction for something that doesn't have to be abstracted (since you've already abstracted away the container using the Global.Resolve<T>).

Unfortunately with Web forms, there is not really any good way to do this. For Simple Injector, I wrote an integration guide for Web Forms that basically describes the use of the Global.Resolve<T> method, but also shows a way to tests if Page classes can be created. The guide can be used for other DI containers as well.

BTW, please keep in mind that with Castle Windsor, everything you request must be released explicitly (the Register Resolve Release pattern). This is a bit nasty (IMO) and differs from how other containers work and can be a source of memory leaks when you do not do this correctly.

Last note. It is possible to do constructor injection with Web Forms. Well... sort of, since this will call the overloaded constructor using reflection after the Form has been created using the default constructor, so this causes Temporal Coupling.

4
votes

Is there a more elegant solution for using DI with Webforms?

Yeap, the MVP pattern allows you to have a clean separation of concerns in a WebForms application. And once you have separation of concerns and weak coupling, DI is easy.

And in ASP.NET MVC that's built-in.

4
votes

Know that this is pretty old, but now, there is DI in WebForms starting in .NET 4.7.2. Regarding to this article: ASP.NET Blog: Use Dependency Injection In WebForms Application

Just install Microsoft.AspNet.WebFormsDependencyInjection.Unity package and registr your classes in Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    var container = this.AddUnity();

    container.RegisterType<IPopularMovie, MovieManager>();
    container.RegisterType<IMovieRepository, XmlMovieRepository>();
}

Hope it help.

2
votes

ASP.NET MVC has IDependencyResolver and a static manager class that lets you get and set the resolver. I didn't like the idea of referencing System.Web.Mvc in a web forms project, so I went with IServiceLocator, which does about the same thing:

public static class Bootstrapper
{
    private static readonly IUnityContainer _container = new UnityContainer();

    public static void Initialize()
    {
        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container));

        _container.RegisterType<IDriverService, DriverService>();
    }

    public static void TearDown()
    {
        _container.Dispose();
    }
}

public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        Bootstrapper.Initialize();
    }

    protected void Application_End(object sender, EventArgs e)
    {
        Bootstrapper.TearDown();
    }
}

Then in your Page class ...

IDriverService service = ServiceLocator.Current.GetInstance<IDriverService>();

Or wire up DI via constructor injection. I haven't gone down that road with web forms yet, so someone else will need to fill in for me :) (I've been living mostly in MVC land for about a year now).

My example uses Unity, but you should be able to adapt it to any other DI implementation fairly easily.

1
votes

As @DarinDimitrov says the MVP pattern is the way to go in order to use DI/IOC with Webforms.

Either you can roll your own implementation or use an existing framework. I've heard good about Webforms MVP, but I haven't actually used it.

According to the docs, it has built in support for DI via Castle Windsor, Autofac and Unity. It also has convention based auto discovery for Presenters.

0
votes

Actually, what you have just built is your own implementation of the Service Locator. But, almost for sure, an implementation already exists for a IoC framework of your choice.

http://commonservicelocator.codeplex.com/