Object Oriented ASP.Net

While there are a huge number of articles about ASP.Net online very few of them discuss structuring pages and controls for readability and maintainability.  What general advice can we give to our programmers for building better web sites?  There is one key thing a lot of people seem to be missing:

Pages, master pages and controls are all objects and should be designed with the same object oriented principles you are already using elsewhere.

This post is based on a simplified version of a page one of our programmers was building at work.  The master page had a toolbar along the top with new, save and back buttons.  The page had a series of tabs for accessing different tools and the content of each tab was represented by a user control.  The code in question was in each of the user controls and looked something like this:

public partial class WebUserControl1 : UserControl, ITool

{

    protected void Page_Load(object sender, EventArgs e)

    {

        ((Button)Page.Master.FindControl("btnSave")).Click += Save;

    }

 

    void Save(object sender, EventArgs e) ...

The remainder of this post will discuss applying some of these principles to the code above.

Encapsulation

The first problem with the code above is that it relies on the master page being implemented a particular way and if it changes we wont know about it until run time.  Let's have the master page expose the page button via a strongly typed property whose implementation we can change without affecting any code using it:

((Site1)Page.Master).SaveButton.Click += Save;

This is a little better but out control still depends on being hosted on the right sort of master page.

Dependencies

Our user control depends far too much on being hosted in the right sort of page using the right sort of master page.  This greatly reduces the flexibility of the control making it harder to use, harder to test and harder to develop.  In general, the less our control assumes about the outside world the better.

Since there has already been plenty written about these ideas here are a couple of starting points:

With those ideas in mind let's take the master page out of the picture completely and instead require that our host page provide a Save event for us to subscribe to:

((WebForm1)Page).Save += Save;

We've certainly reduced our assumptions about the world around us but we are still casting to a particular type of page.  We could extract the event into an interface that must be passed to the user control which would be a form of dependency injection.  In this case I want to consider a related concept which can often simplify things for us.

Inversion of Control

Until now our user control has required an event so that it can listen to save events.  Let's consider for a moment what the single responsibility of the control is.  I'm going to go with:

To display information for editing and saving.

Note that our description doesn't mention the lifecycle of the page or interaction with other objects at all.  To allow for these let's define the pages responsibility:

To decide when and how each hosted tool interacts with the user.

Most importantly the details of each tool are not required for the page to do it's job.  Let's take control away from the user control and instead say that so long as it provides a save method the page will call it at the appropriate time:

public partial class WebUserControl1 : UserControl, ITool

{

    void Save() ...

 

public partial class WebForm1 : Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        Master.Save += Save;

    }

 

    void Save(object sender, EventArgs e)

    {

        currentTool.Save();

    }

Conclusions

Our user control no longer assumes anything at all about the environment it is hosted in making it much more flexible.  We have moved all of the life cycle logic into the page where we can write it once and it will be reused for any tool the page hosts.  There are no strings, casts or guesses about how the master page is implemented.

Give your ASP.Net interface objects as much thought as you do the rest of your code.  Future maintainers will appreciate it.

Labels: ,

12/04/2008 11:04 PM (UTC -07:00)
Comments are closed.