WebForms is an awkward marriage between a Page Controller and a Template View. In the Web Forms model the Template View (aspx page) inherits from the Page Controller (code behind).
Patterns of Enterprise Application Architecture defines the Page Controller as:

“An object that handles a request for a specific page or action on a web site.” - PoEAA

In this example I’ve separated the Page Controller from the Template View, because… mostly because I was bored and thought this would be a great way to better understand the patterns. So let’s get started…
I defined a layer super type for all page controllers defined as :

1 public interface IPageController : IHttpHandler {
2         void Execute();
3     }

The IPageController could have very well been called and Page Command because in this implementation I’m not concerned about having separate behaviors for GET and POST method requests. If this example were to evolve I might choose to separate the “Execute()” method into “ProcessGetRequest()” and “ProcessPostRequest()”.

The IPageController type inherits IHttpHandler in order to register this page controller with ASP.NET to receive all requests for a particular path. In this case this handler is registered in the web.config for all requests to the “DisplayAllCustomers.aspx” page.

1 <httpHandlers>
2         <add 
3             verb="*" 
4             path="DisplayAllCustomers.aspx" 
5             validate="false" 
6             type="PlayingWithPageControllers.Web.Controllers.DisplayAllCustomersController, PlayingWithPageControllers"/>
7     </httpHandlers>

If I wanted to get a little more nitty, gritty I could have specified only GET Http method’s are handled by this handler.

Moving on…. The PageController base type for all controllers looks like:

 1 public abstract class PageController : IPageController {
 2         public void ProcessRequest( HttpContext context ) {
 3             Execute( );
 4         }
 5 
 6         public bool IsReusable {
 7             get { return true; }
 8         }
 9 
10         public abstract void Execute();
11     }

And finally our “DisplayAllCustomersController” component looks like:

 1 public class DisplayAllCustomersController : PageController, IDisplayAllCustomersController {
 2         public DisplayAllCustomersController( IDisplayAllCustomersView view, ICustomerTasks tasks ) {
 3             _view = view;
 4             _tasks = tasks;
 5         }
 6 
 7         public override void Execute() {
 8             _view.AddToBag( _tasks.AllCustomers( ) );
 9             _view.Render( );
10         }
11 
12         private readonly IDisplayAllCustomersView _view;
13         private readonly ICustomerTasks _tasks;
14     }

And voila all requests to “DisplayAllCustomers.aspx” are handled by the DisplayAllCustomersController which pulls information from the model and fires it off to the template view to be rendered.

The Template View Pattern is defined as:

“Renders information into HTML by embedding markers in an HTML page.” - PoEAA

Our template view for “AllCustomers.aspx” looks like this:

<table>
        <thead>
            <tr>
                <td>First Name:</td>
                <td>Last Name:</td>
            </tr>
        </thead>
        <tbody>            
            <&#37; foreach ( DisplayCustomerDTO dto in ViewBagLocator.For( ViewBagKeys.DisplayCustomers ) ) {&#37;>
            <tr>
                <td><&#37;= dto.FirstName() &#37;></td>
                <td><&#37;= dto.LastName( ) &#37;></td>
            </tr>
            <&#37; } &#37;>
        </tbody>
    </table>

This separates the Template view from having any knowledge of the Page Controller. The Page controller has the responsibility of pulling information from the model to pass along to the appropriate view.

All access to the current context and the ASP.NET pipeline has been isolated to the HttpGateway which abstracts the ASP.NET facilities available through a condensed client interface.

1 public interface IHttpGateway {
2         void RedirectTo( IView view );
3 
4         void AddItemWith< T >( IViewBagKey< T > key, T itemToAddToBag );
5 
6         T FindItemFor< T >( IViewBagKey< T > key );
7     }

Now that I think about it, another step that I could have taken would have to shield the page controllers from having any knowledge of “IHttpHandler”, which would have further isolated the ASP.NET infrastructure from the rest of the web presentation layer.

Patterns of Enterprise Application Architecture defines a Gateway as:

“An object that encapsulates access to an external system or resource.” - PoEAA

 1 public class HttpGateway : IHttpGateway {
 2         public HttpGateway( IHttpContext context ) {
 3             _context = context;
 4         }
 5 
 6         public void RedirectTo( IView view ) {
 7             _context.Server.Transfer( view.Path( ) );
 8         }
 9 
10         public void AddItemWith< T >( IViewBagKey< T > key, T itemToAddToBag ) {
11             _context.Items.Add( key, itemToAddToBag );
12         }
13 
14         public T FindItemFor< T >( IViewBagKey< T > key ) {
15             return ( T )_context.Items[ key ];
16         }
17 
18         private readonly IHttpContext _context;
19     }

If you haven’t already you should go buy and read, then re-read, then re-read “Patterns of Enterprise Application Architecture by Martin Fowler”

Patterns of Enterprise Application Architecture (The Addison-Wesley Signature Series)

by Martin Fowler Read more about this title…

DOWNLOAD THE CODE

comments powered by Disqus