Extending the Page Object in ASP.net

I was once asked the following question during an interview for an asp.net developer role;

“How would you go about achieving common functionality and members across multiple pages?”

I was offered the job, and later found out that my answer to this question had been instrumental in the decision to hire me. Around 4 months later, I was interviewing developers myself and decided to throw the same question into the mix. Every single person gave the same answer, which was;

“Master Pages”

Nooooo! A quick google of this reveals that it’s a point that many asp.net programmers seem to get stuck on, so I’ve chosen it as the topic of my first post. In this article I’m going to explain why that wasn’t the correct answer, and then move onto some neat patterns for extending  the page object as well as some ideas and examples.

Why shouldn’t I use a Master Page?

Master pages are incredibly useful when used for the correct purpose, but this purpose is largely limited to the presentation layer of your website.You can kind of see why so many people think that Master Pages are a good way of achieving common functionality across pages. The name MasterPage itself heavily implies there is a releationship and that the Master Page is higher up the heirachy. To the unaccustomed eye, the master page is a container which surrounds content stored on other pages, so the logical conclusion to jump to is that the master page is a parent of the page object. In actual fact, the complete opposite is true.

The first common ancestor that the master page and the page objects have is the TemplateControl class , where the page object is a direct extension of it and the MasterPage object is an extension of one of its children (The UserControl object). As they’ve gone down two seperate lineages, there is no way they can be related in the traditional sense.

You might then jump to the conclusion that the MasterPage is related to a page through possessing a reference to the page as a member. Again, this is not correct. The master page, in fact, has no architectural relationship to the page object at all, and accessing elements on a page from a master page is a colossal pain in the neck. You can only really do it using the FindControl method on your ContentPlaceHolders, which is not only incredibly inefficient and messy but also means you suddenly have to be very careful about the name you give each control’s ID across your entire site. The compiler will not be able to spot errors of this kind, and you could be setting yourself up for a massive headache later on.

In actual fact, the master page is accessed through a property of the Page object, which makes accessing the properties of a master page incredibly simple from within a page. For this reason, you should think of a master page as actually being a child of a page, not vice versa. Given that this is the case, it should be fairly clear why Master Pages are therefore not the place to put shared functionality.

Extending the page object

In order to achieve common functionality across multiple pages, you can simply inherit from the Page object.

public class ExtendedPage  : System.Web.UI.Page
{
    protected string _aMember;

	public ExtendedPage()
	{
              _aMember = “something we need on every page”;
	}
}

And then just inherit from that class in your actual pages;

public partial class _Default : ExtendedPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
	  string s = _aMember;
    }
}

Remember also that you have access to the full page life cycle, you can and should use this to do most of your initialization within your new base class. Code-behind files within asp.net websites, particularly the page_load method, should really only contain code relevant to building the page you are looking at (for example, the page_load event above should only contain code for building the _Default page, and should not contain initialization code for an ExtendedPage). You can achieve this very easily using event handlers;

public class ExtendedPage  : System.Web.UI.Page
{
    protected string _aMember;

	public ExtendedPage()
	{
        Init += AFunction;
	}

    private void AFunction(Object sender,EventArgs e)
    {
        //any functionality
    }
}

This provides a tidy method of getting functionality across multiple pages and keeps all of the initialization out of the code-behind files.

Examples of Usage

I would strongly advocate that you don’t just extend the page object once and use that everywhere. You can use the exact same techniques described here to extend the page multiple times. Probably the best real world example I can think of is the idea that your website might have some pages which require a user to be “logged in” before he or she can view them. You could achieve this very easily by inheriting from your “ExtendedPage” object above, perhaps calling it a “LoggedInPage” and putting functionality in its initialization routines to check for the ‘logged-in-ness’ of your user whichever way you want to, redirecting them to a different page if they’re not. After you’ve done this you can simply inherit from “LoggedInPage” for all those pages that require a login.

Another good use of this would be for managing database connections for the duration of a pages lifetime. You may not want a database connection on every page, so again you could create a new page class that inherits from your ExtendedPage, and use the event handlers to open the connection on the Load event and close it on the Unload event. As the unload event will always fire, you can be sure that you are always closing your connections which reduces the chances of a DB connection related memory leak to zero. You might not want to have your database connection on the page itself, for example you may want to abstract this concept into the seperate layer so you can easily change your data source type should you need to. The general idea described here can be applied to that situation too, I’ve kept it as a database connection in the example for simplicity.

And back on to master pages for a second: Maybe you do want access to some of the page’s properties from within a master page? For example, the aforementioned database connection might be pretty useful on the master page. To achieve this you could simply put a bunch of properties on your master page object (through inheritence or the use of an interface) and set them within an event handler of a page (of which, remember, the master page is a member).

public class ExtendedPage  : System.Web.UI.Page
{
    SqlConnection _connection;

	public ExtendedPage()
	{
        Init += StartConection;
        Init += MasterProperties;
        Unload += EndConnection;
	}

    private void MasterProperties(Object sender, EventArgs e)
    {
        var master = (MyMaster)this.Master;
        master.Connection = _connection;
    }

    ... other event handlers to open/close connection
}

In terms of members, there may be information that you want to use on all pages, such as the site root and the sites home page. For code readability it might even be nice to get some of the things that you commonly access in the request object and stick them into member variables. Without naming any names, it might also be that you want to integrate a third party tool or widget into your code, and that this has to appear on every page of your site, and you could do this in your page object without the need to put any code into your master page at all.

Conclusion

The answer to the question right at the top of this article is therefore;

“the best way to achieve common functionality and members across multiple pages is to extend the page object”

Hopefully the advantages of doing things this way are pretty clear now. The sky is pretty much the limit once you get your page object architecture together, which is something that it’s worth spending a bit of time on to get right.

Advertisements

7 thoughts on “Extending the Page Object in ASP.net

    • I personally don’t like to think of the code-behind of a page as being part of the UI layer (the UI layer literally only being the aspx/ascx page) but each to their own.

      Of course, any connection object on a page should be passed down to a secondary business layer before any commands are run on it, but within the context of creating a page, the opening and closing of connections within the init and unload events doesn’t seem like too bad a design to me given the tools we’re working with here. Hopefully if MVC is adopted en-masse then we’ll be rid of code behinds altogether and the above principles can be applied to overriding controllers instead.

      Anyway – thank you for reading the article!

    • Had a bit more of a think about this: Your page loading and unloading is going to have to give a bit of a nudge to your business layer anyway to tell it to open a connection, so it’s going to have to have some kind of awareness of what a database is. If you do it this way you’re sure of having only one connection per request, and as these things are a limited resource there is something to be said for doing it the way suggested here, even if it does break the seperation of concerns a little bit (but only if you consider code-behind/inline code to be part of the ui layer)

  1. With your example if I wanted the application to use a different data storage mechanism such as an XML repository possibly for testing then my UI would need to be updated to reflect this change. In my opinion my UI which code behind is because it is located in the web site assebmly should have no knowledge of the underlying storage machanism. It should only be aware of the business service it is calling and the expected busines object returned.

    • Okay dude you are right. Lets say we were to replace the database connection with some kind of Factory that creates business objects, stick open the connection in its constructor and close it in the destructor, then create/explicitly dispose of this Factory in the places I’m opening and closing the connection in the example above, then the page has no knowledge of a database and all you’d have to do to attach to another data source is just to write a new Factory.

      I’m not going to change the example though as this wasn’t the point I was trying to demonstrate and I don’t really want to complicate it, but I will go and put a comment in the article.

      Thanks again,

      Mikey

  2. I really like this article. I was wondering, in your experience, is there a way of setting Visual Studio to automatically use your new extended Page class instead of System.Web.UI.Page when creating a new page? Saves on having to manually change every time!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s