Skip navigation.
Suppose we have a Contact Us page. Old school, with all the logic in code-behind. I borrowed it from an old project, but I’ve written plenty of those myself over the years, and I’m sure so have you.
public partial class Contact1 : Page { private void SubmitFeedback (object sender, EventArgs e) { if (!Page.IsValid)s return; string subject; string to, from; MailMessage message; StringBuilder body; subject = Subject.Text; body = new StringBuilder (); body.Append ("Hello!" + "<br /><br />"); body.Append ("Thank you for stopping by at Acme.com."); body.Append ("We will get back to you shortly." + "<br /><br />"); body.Append ("Sincerely," + "<br /><br />"); body.Append ("The Acme Team" + "<br /><br />"); body.Append ("NOTE: Please do not reply to this message.<br />"); from = ConfigurationManager.AppSettings["SupportEmail"]; message = new MailMessage (); message.From = new MailAddress (from); message.To.Add (new MailAddress (Email.Text)); message.Subject = subject; message.Body = body.ToString (); message.IsBodyHtml = true; SmtpClient client = new SmtpClient (); client.Send (message); // ------------------------------------ to = ConfigurationManager.AppSettings["SupportEmail"]; subject = Subject.Text; body = new StringBuilder (); body.Append ("First Name: " + FirstName.Text.Trim () + "<br />"); body.Append ("Last Name: " + LastName.Text.Trim () + "<br />"); body.Append ("Email: " + Email.Text.Trim () + "<br />"); body.Append ("Comment: " + Comment.Text.Trim () + "<br />"); body.Append ("<br />"); message = new MailMessage (); message.From = new MailAddress (Email.Text); message.To.Add (new MailAddress (to)); message.Subject = subject; message.Body = body.ToString (); message.IsBodyHtml = true; client = new SmtpClient (); client.Send (message); Response.Redirect ("~/ThankYou.aspx"); } protected override void OnInit (EventArgs e) { base.OnInit (e); SubmitContact.Click += SubmitFeedback; } }
This is the kind of code we’ve come to loathe and this is why WebForms have a bad reputation.
We have too many concerns here:
This code is begging for refactoring.
Here are my goals for this exercise:
It genuinely surprises me how many people cannot define refactoring during interviews. The idea is simple, really:
“Refactoring” source code means improving it without changing its overall results. The process could be informally referred to as “cleaning up” or “taking out the garbage.” Refactoring neither fixes bugs nor adds new functionality, though it might precede either activity. Rather, it improves the understandability of the code, changes its internal structure and design, and removes dead code.
Here’s what the same page may look like after some reorg:
public partial class Contact2 : Page { private void SubmitFeedback (object sender, EventArgs e) { if (!Page.IsValid) return; SendConsumerEmail (); SendSupportEmail (); Response.Redirect ("~/ThankYou.aspx"); } private void SendConsumerEmail () { string subject = Subject.Text; body = new StringBuilder (); body.Append ("Hello!" + "<br /><br />"); body.Append ("Thank you for stopping by at Acme.com."); body.Append ("We will get back to you shortly." + "<br /><br />"); body.Append ("Sincerely," + "<br /><br />"); body.Append ("The Acme Team" + "<br /><br />"); body.Append ("NOTE: Please do not reply to this message.<br />"); string from = ConfigurationManager.AppSettings["SupportEmail"]; new EmailManager () .SendEmail (Email.Text, from, subject, body.ToString ()); } private void SendSupportEmail () { string to = ConfigurationManager.AppSettings["SupportEmail"]; string subject = Subject.Text; StringBuilder body = new StringBuilder (); body.Append ("First Name: " + FirstName.Text.Trim () + "<br />"); body.Append ("Last Name: " + LastName.Text.Trim () + "<br />"); body.Append ("Email: " + Email.Text.Trim () + "<br />"); body.Append ("Comment: " + Comment.Text.Trim () + "<br />"); body.Append ("<br />"); new EmailManager () .SendEmail (to, Email.Text, subject, body.ToString ()); } protected override void OnInit (EventArgs e) { base.OnInit (e); SubmitContact.Click += SubmitFeedback; } }
Most of the same “concerns” still live in the page, with the exception of EmailManager whose purpose must be obvious. No magic yet.
EmailManager
What if I want to move all dependencies off the page and have them provided from the outside? This way the page will be left to its devices to manage postback, interact with controls, etc.
I have described this thought process in an earlier article of mine, Inversion of Control and Dependency Injection with WebForms (In 2 Acts). Please read it, if you haven’t yet. Also, if the concepts of Inversion of Control (IoC) and Dependency Injection (DI) are new to you, you do need to read the article.
What I have in mind is this:
public partial class Contact3 : Page { public IContactProcessor ContactProcessor { get; set; } private void SubmitFeedback (object sender, EventArgs e) { if (!Page.IsValid) return; ContactInfo contactInfo = new ContactInfo { Subject = Subject.Text, FirstName = FirstName.Text, LastName = LastName.Text, ConsumerEmail = Email.Text.Trim (), Comment = Comment.Text }; ContactProcessor.Process (contactInfo); Response.Redirect ("~/ThankYou.aspx"); } protected override void OnInit (EventArgs e) { base.OnInit (e); SubmitContact.Click += SubmitFeedback; } }
What’s going on here?
The page collects personal info (PI) and passes it on to some Contact Processor. The page is not concerned with what the processor does and how.
In this context, ContactInfo is a simple class which shuttles PI. In refactoring terms, it’s a Parameter Object.
ContactInfo
The Contact Processor may send emails, or call web services, or store PI in a database. The point here is the page doesn’t worry about it, because it’s not its business. We achieve this by getting the page to talk to the Contact Processor through a contract—an interface.
public interface IContactProcessor { void Process (ContactInfo contactInfo); }
You may have noticed that the page doesn’t create an instance of a Contact Processor. It has no idea what concrete implementation it’s going to work with. An instance of IContactProcessor will be handed to it by an IoC container, the mechanics of which I’ll discuss later.
IContactProcessor
ContactProcessor, in its turn, doesn’t concern itself with irrelevant concerns of creating, formatting, and dispatching emails.
ContactProcessor
public class ContactProcessor : IContactProcessor { private readonly IEmailManager _emailManager; private readonly IEmailFactory _emailFactory; public ContactProcessor ( IEmailFactory emailFactory, IEmailManager emailManager) { _emailFactory = emailFactory; _emailManager = emailManager; } public void Process (ContactInfo contactInfo) { SendConsumerEmail (contactInfo); SendSupportEmail (contactInfo); } private void SendConsumerEmail (ContactInfo contactInfo) { MailMessage email = _emailFactory.CreateContactConfirmationEmail (contactInfo); _emailManager.SendEmail (email); } private void SendSupportEmail (ContactInfo contactInfo) { MailMessage email = _emailFactory.CreateContactEmailToSupport (contactInfo); _emailManager.SendEmail (email); } }
Again, dependencies external to ContactProcessor are injected by an IoC container. You can inspect their implementation in the code download.
You can add layers of abstraction ad nauseum, so you have to develop a feel for when to stop. I could refactor email formatting into its own class, but I chose not to.
At some point, you’ll have to read app settings from web.config. For example, the mailer needs to know the reply-to and support emails. How do you break the dependency on ConfigurationManager to make the code testable?
ConfigurationManager
What I normally do is define an interface, IConfigurationProvider, and declare only the settings I’m interested in.
IConfigurationProvider
public interface IConfigurationProvider { string ReplyToEmail { get; } string SupportEmail { get; } }
This allows me to create a configuration provider mock in my unit tests without any dependency on web.config, whereas the real ConfigurationProvider goes about its business as usual.
ConfigurationProvider
public class ConfigrationProvider : IConfigurationProvider { public string ReplyToEmail { get { return ConfigurationManager.AppSettings["ReplyToEmail"]; } } public string SupportEmail { get { return ConfigurationManager.AppSettings["SupportEmail"]; } } }
Remember the trouble I went to in my previous article creating a custom PageHandlerFactory, resolving dependencies by hand and so forth?
PageHandlerFactory
This is where Spring.NET—an open-source IoC container—shines: it does it all for you! I has its own PageHandlerFactory, it works with WebForms, user controls (.ascx), HttpModules, HttpHandlers, WCF, etc. You can read more about it in Chapter 22, Spring.NET Web Framework of the official docs. In truth, Spring.NET has so much more to it, but here and now we’ll focus on its web facilities.
Spring’s configuration is XML-based (surprise!) which actually comes in handy when you maintain config files for various environments (dev, QA, stage, prod). Don’t try to memorize the settings—they are all in the docs.
To enable Spring.NET I’ve added the following entries in my web.config:
<configuration> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/> </sectionGroup> </configSections> <system.web> <httpHandlers> <add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> </httpHandlers> <httpModules> <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> </httpModules> </system.web> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <modules> <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/> </modules> <handlers> <add name="SpringPageHandler" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/> <add name="SpringContextMonitor" verb="*" path="ContextMonitor.ashx" type="Spring.Web.Support.ContextMonitor, Spring.Web"/> </handlers> </system.webServer> <spring> <context> <resource uri="~/Config/Spring.config"/> </context> </spring> </configuration>
You will find these settings in sections 22.3.1. Configuration and 22.3.1.1. Configuration for IIS7 of the docs.
I chose to register all dependencies in a separate file, Spring.config.
<objects xmlns="http://www.springframework.net" default-autowire="byName"> <object id="emailFactory" autowire="constructor" type="ContactUs.Infrastructure.Services.EmailFactory,» ContactUs.Infrastructure" /> <!-- the rest omitted --> <object type="~/Contact3.aspx" /> </objects>
Note that web pages have a somewhat odd notation. The type attribute is the path to an .aspx file and there’s no id attribute.
type
id
You may have noticed that ContactProcessor is injected onto the page via a property setter, whereas ContactProcessor itself takes everything in its constructor.
One school of thought dictates that dependencies injected via properties should be treated as optional. I disagree. First, I don’t quite understand why you’d have optional code. Second, we’re dealing with ASP.NET, which has a life of its own, so I don’t call the shots. Hence, my only recourse is to use properties.
On the other hand, I’m the sole owner of ContactProcessor so I can pass dependencies to its constructor. The signature of such a constructor helps me see if dependencies are coherent, i.e. related to each other. If not, perhaps my class has one too many concerns and it’s time to refactor it.
To me, which approach to use is a matter of expediency, not dogma. At any rate, Spring.NET can automatically wire both.
It’s not feasible or reasonable to test WebForms, so I won’t even go there. By now, my WebForms should have a bare minimum of logic, with each external dependency standing on its own and placed under test.
As a matter of personal preference, I chose NUnit, an open-source unit-testing framework, and Moq, a light and simple mocking library.
I encourage you to peruse the test project on your own.
I hope this article helps you rethink how you build WebForms. The name of the game here is to identify dependencies, move them out of the WebForm and let the page do what it does best. This way you kill several birds with one stone: your code becomes less coupled and more cohesive, and you can put more of it under test than before.
Whereas my previous article was highly experimental, the ideas set forth here applicable to the here-and-now. Historically, ASP.NET has been a very closed framework, so we need some help peeling the layers which is where Spring.NET helps immensely.
Liked it? Hated it? Discuss this article