Webforms and Dependency Injection

11,855

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

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.

Share:
11,855
Phil Sandler
Author by

Phil Sandler

Senior Technical Architect working as an independent consultant in the Chicago area.

Updated on June 05, 2022

Comments

  • Phil Sandler
    Phil Sandler almost 2 years

    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?

  • Steven
    Steven over 12 years
    Is it really useful to do a TearDown on application end? The app domain is being unloaded anyway. It's only useful if you registered singleton services that have teardown logic in their Dispose method, but would still be fragile, since there is no guarantee that all those Dispose methods actually run when an AppDomain is unloaded.
  • jrummell
    jrummell over 12 years
    Good question. I was simply trying to follow the disposable pattern. I wasn't sure where else to put it other than application end.
  • Phil Sandler
    Phil Sandler over 12 years
    Thanks for your answer. That last article is interesting, and would indeed be of great interest if not for the full-trust problem.
  • Steven
    Steven over 9 years
    @PhilSandler: Seems that Microsoft has abandoned partial trust completely from ASP.NET 4.0 and beyond.
  • Issa Fram
    Issa Fram about 9 years
    If the Resolve<T> method is static, then it has no access to the instance member container. I might be misunderstanding you though. Do you recommend making the container static as well?
  • Endy Tjahjono
    Endy Tjahjono over 4 years
    If you can upgrade to .net framework 4.7.2 there is support for dependency injection. Example of integrating SimpleInjector into webforms.