Dependency injection with WebForms
Ack, WebForms! Burn it with fire!
WebForms is known to be a horrible ghetto full of HTML pitfalls, a Kafka-esque lifecycle, testability nightmares, and pages served up with inflated ViewState data.
Some of that is still true, but WebForms has improved, and despite all the horrors that we're familiar with, it's still got a lot going for it, including a control tree that can be very useful at times.
I have been working on a WebForms project in my independent consulting practice (it sounds so fancy when I say it like that). Even though it's a relatively small project, I was still thinking about how best to introduce dependency inversion [PDF] to the project (or if I even should).
I googled around, sifted through a lot of ancient blog posts, and I came up with a relatively simple solution with my favorite IoC tool, StructureMap. It's not really optimal, or terribly elegant, but it just might get the job done and still allow us to write tests. I'm certainly open to alternatives, but because this is a small project, I don't want to spend a ton of billable hours on a solution, especially when I'll be turning the code over to novice programmer(s) when my time is done.
First, I simply add standard StructureMap config to the project. Application_Start in Global.asax is as good a place as any. I also use the default convention (if you aren't familiar, it's basically naming the interface by prepending an "I" to the concrete class name).
Okay, so that's pretty standard. Now let's approach from the other end: an actual code-behind class of an ASPX page. I can't use constructor injection here, because I don't have much control over this object being instantiated. Instead, I'll just make public properties for my service(s).
So now I've got a StructureMap defintion, and I've got properties that want to be set by StructureMap. But how do I get them talking? My solution was to introduce a base class (bleck) to the WebForms page. In this constructor, I'll use StructureMap's BuildUp method to populate properties (I have to specify which ones, as noted later on).
That's the ugliest part, as far as I'm concerned, but I couldn't think of a better solution. Maybe an HttpModule or something? Maybe the newer WebForms releases contain something that I'm not familar with yet? Leave a comment with your suggestions.
Finally, StructureMap just won't go and set every property by itself. You need to specify which ones. There are a couple of ways to do this. One is the SetAllProperties, which allows you to conditionally examine the PropertyInfo, and the other is FillAllPropertiesOfType, which you can use to directly specify which properties to set based on what type they are. Even though it's a small project, I'd would prefer to use SetAllProperties, so I don't have to go back and edit my StructureMap config each time I create a new service or a new WebForms page.