Inversion of Control and Dependency Injection with WebForms (In 2 Acts)

Posted on November 14, 2009   |   Download sample code

3 comments

When just about everybody has written off WebForms as uncool and hailed MVC as the new Silver Bullet, I beg to differ.

Act 1, Scene 1: The Devil’s Advocate

That’s right—WebForms. Since when is application of a principle (not principal!) is tied exclusively to a specific tool or technology?

None of the principles of responsible software design—known as S.O.L.I.D.—is monopolized by MVC. It’s just that the community around MVC is more vocal.

I’ve argued before and I’ll argue again: yes, you can write lean apps with the now-“classic” ASP.NET. Often times, it’ll make no sense to destroy your investment in a WebForms app. I believe only a small percentage of ASP.NET apps will be written with MVC. I’m also looking forward to the 4.0 release where some of the old-standing pain points will be finally alleviated.

Act 1, Scene 2: Testability

No, you can’t unit test pages which heavily rely on HttpContext (request, response, cache, etc). But when do you use all of the properties of those objects?

The biggest obstacles on the path to testability have been view state and postbacks—the two techniques that made WebForms possible.

Naturally, then, you need to wean yourselves off view state completely. “But wait, I can’t use my DataGrid without view state!”

Exactly.

You’ll be forced to look for cleaner, lighter alternatives. Maybe you’ll finally realize that client-side development belongs… on the client-side, not behind heavily fortified server-side abstractions.

Next, postbacks.

With view state out of the picture, your cravings for postbacks will diminish. I’m not against them per se. Sometimes a page refresh is better than a redirect. “But I can’t unit test postbacks!”

Precisely.

Postbacks work, we know that much. View state works. No need to unit test them. And, besides, why are you running code you want to test in pages? Leave them to deal with setting and scraping values from server controls. Just about everything else is an external dependency which needs to:

  1. live outside of your web code
  2. be provided (read “injected”) when you need it

See? We’re getting somewhere.

Act 2. Scene 1: A custom page handler factory.

A common misconception is that you can’t affect how a WebForm comes to existence. Peek inside your global web.config:

<add
    path="*.aspx"
    verb="*"
    type="System.Web.UI.PageHandlerFactory" validate="True"/>

This PageHandlerFactory is a crucial player in a request.

“[PageHandlerFactory creates] instances of classes that inherit from the Page class and implement the IHttpHandler interface. Instances are created dynamically to handle requests for ASP.NET files.

[It] calls the ASP.NET compilation system to compile, if necessary, and return the exact type corresponding to the URL, and then creates an instance of that type.”

You can even create one of your own.

“To build a custom page handler factory, implement the IHttpHandlerFactory and register the custom PageHandlerFactory class in the Web.config file for the application in the httpHandlers Element configuration section.”

What I had in mind was create a factory to resolve external dependencies on pages:

public override IHttpHandler GetHandler (
   HttpContext context, string requestType, 
   string virtualPath, string path)
{
  Page page = 
   base.GetHandler (context, requestType, virtualPath, path) as Page;

  if (page == null) 
    return null;
	
  // TODO: Need to do something to get the page to resolve 
  // external dependencies, if any

  return page;
}

This is where the big idea of Inversion of Control (Ioc) and its particular implementation, called Dependency Injection (DI), come into play. If you need to take a breather and brush up on IoC and DI, go read Martin Fowler’s seminal post, Google around for a shorter explanation, or peruse the slides from my recent presentation.

To date, I’ve been a heavy user of the Spring.NET IoC container. For this experiment, I chose StructureMap for the very scientific reason that I simply wanted to get acquainted with it.

It turned out, StructureMap was able to “build up” an instance of a page with any dependencies.

public class CustomPageHandlerFactory : PageHandlerFactory
{
  public override IHttpHandler GetHandler (HttpContext context, 
       string requestType, string virtualPath, string path)
  {
    Page page = 
     base.GetHandler (context, requestType, virtualPath, path) 
       as Page;

     if (page == null) 
      return page;
	
    IoC.BuildUp (page);
    return page;
  }
}

The IoC class you see above is something I borrowed from David Hayden’s idea of a generic facade, of a sorts.

public static class IoC
{
  static IoC ()
  {
    ObjectFactory.Initialize (x =>
      x.AddRegistry (new DependencyRegistry ())
	);
  }

  public static T Resolve<T> ()
  {
    return ObjectFactory.GetInstance<T> ();
  }

  public static T Resolve<T> (string name)
  {
    return ObjectFactory.GetNamedInstance<T> (name);
  }

  public static void BuildUp (object target)
  {
    ObjectFactory.BuildUp (target);
  }
}

Here’s the dependency registry:

public class DependencyRegistry : Registry
{
    public DependencyRegistry ()
    {
      Scan (scanner =>
     { 
        scanner.Assembly ("NewHandlerFactory.Web");
        scanner.Assembly ("NewHandlerFactory.Services");
        scanner.WithDefaultConventions ();
      });

      SetAllProperties (y => y.OfType<IFoo> ());
    }
}

Act 2. Scene 2: Pages with dependencies.

The way I write WebForms today is by trying to move all non-web code out of my pages. Postbacks and view state are not an issue if your code doesn’t depend on them.

Suppose I have a sophisticated service, Foo, whose behavior I hide behind an interface I call IFoo:

public interface IFoo
{
  void Bar ();
}

public class Foo : IFoo
{
  public void Bar ()
  {
    // Extremely helpful logic goes here
  }
}

I want to decouple my page from Foo. I don’t want the page to know implementation details of Foo. Let them talk through an interface. If the page needs its services (no pun intended), the page will call it at the opportune moment. Therefore, I need to make IFoo available to the page and have it instantiated somewhere.

My sample page looks as follows:

public partial class HomePage : Page
{
  public IFoo Foo { get; set; }

  protected override void OnLoad (EventArgs e)
  {
    base.OnLoad (e);
    Foo.Bar ();
  }
}

Since I want my CustomPageHandlerFactory to do the heavy lifting, I need it registered in web.config.

<add 
  path="*.aspx" 
  verb="*"
  type="NewHandlerFactory.Web.ComponentModel.Runtime.»
        CustomPageHandlerFactory"/>

Now, every time I request the home page, my factory instantiates it, StructureMap looks up what to wire up for IFoo and assigns it to the Foo property.

Since Foo stands on its own, I can place it under test.

User controls are a bit more difficult to wire up. I can think of two approaches: somewhat automatic DI with a superclass, and manual DI.

Act 2. Scene 3: User controls with semi-automatic resolution of dependencies.

Let’s define a base class for all user controls.

public class UserControlWithIoC : UserControl
{
  protected override void OnInit (System.EventArgs e)
  {
    base.OnInit (e);
    IoC.BuildUp (this);
  }
}

Next, any user control has to derive from it.

public partial class ContentWithAutoIoC : UserControlWithIoC
{
  public IFoo Foo { get; set; }

  protected override void OnLoad (EventArgs e)
  {
    base.OnLoad (e);
    Foo.Bar ();
  }
}

Blantatly obvious downsides:

  1. You burn the base class as C# doesn’t allow multiple inheritance.
  2. I wish user controls had a PreInit event or something that fires way early. In your own user control, you have to give base.OnInit(e) a chance to run, as shown.

Act 2. Scene 4: User controls with manual resolution of dependencies.

With manual DI you have more freedom when and where to resolve dependencies. The earlier the better.

public partial class ContentWithManualIoC : UserControl
{
  private IFoo _foo;

  protected override void OnLoad (EventArgs e)
  {
    base.OnLoad (e);
    _foo.Bar ();
  }

  protected override void OnInit (EventArgs e)
  {
    base.OnInit (e);
    _foo = IoC.Resolve<IFoo> ();
  }
}

You resolve each dependency by calling IoC.Resolve with an appropriate type.

Note: you can use the same technique with custom server controls, HttpModules, etc. Alternatively, some IoC containers can do all this for you. Which is the subject of (hopefully) a couple of upcoming posts.

Exeunt

This is my feeble attempt to prove that even with WebForms you can achieve high cohesion by limiting pages to what they do best: manage their life cycle, interact with controls, etc. In the same vein, you can achieve looser coupling by moving out all other code and interacting with it via abstractions. Once on their own, you can place external dependencies under test.

As I always say, I place more value on learning important, durable principles first; passing, fashionable tools—second.

3 comments

Omari Omarov
on November 15, 2009

Brilliant idea.
btw, you can use TemplateControl.FremeworkInitialize instead of OnInit. It is fired on UserControls and Pages very early.


Milan Negovan
on November 15, 2009

Excellent suggestion! I'll give it a try.

This is the kind of feedback I'd like to collect to refine the idea. ;)


Alexander Byndyu
on April 22, 2010

Thanks. You saved my time!