ASP.NET Singleton-per-Page pattern

A while ago I blogged about a pattern useful to ensure that just a single instance of a class was created and used during a unique request-response pair. That seemed to make sense, since using the HttpContext.Items property prevents a single pipeline from instantiating that class more than once.

At the time I didn't realize that actually one could need a little more granularity and may incur in some issues when that instance needs logically to be bound to a single page instead of a single request-response. One might be wondering where's the difference, since usually during a single request just a single page is executed. That's true most of the times unless you use a subtle method of the HttpServerUtility class called Transfer.

When that method is called, usually via the Server property of the Page class, the execution of the current page is interrupted and the control is passed to the new page specified as input parameter, which executes its whole life cycle from the beginning.

In this case both the pages share the same HttpContext, and if in the singleton-per-request instance of the class you got a reference to the currently executing page (that's what I needed to do in the HeadScriptManager), when you transfer the control to the new page your singleton instance still keeps a reference to the old page.

This seems enough to introduce a sub pattern of the singleton-per-request which might be called singleton-per-page. The only problem is to find a place where to store information which is exclusive to a single page and not shared between pages, even if they are executed in the scope of the same context. 

The Page class features a property called Items, an instance of the HybridDictionary class, and that's where we'll store the singleton instance. Here's a sample code to implement the Singleton-per-Page pattern:

public class SingletonPerPage
{
    public static SingletonPerPage Current
    {
        get
        {
            Page currentPage = HttpContext.Current.Handler as Page;      

            if(currentPage == null)
                throw new NullReferenceException("The page is null");

            return (currentPage.Items["SingletonPerPage"] ??
                   (currentPage.Items["SingletonPerPage"] = 
                    new SingletonPerPage())) as SingletonPerPage;
        }
    }
}

From a mere performance point of view this seems a slightly better solution compared to the singleton-per-request, since the Items properties of the HttpContext and Page classes make use of different bags for storing data. As I said, the Page class uses HybridDictionary, a dictionary which internally makes use of the lightweight ListDictionary class until the number of items stored reaches 9, when it switches over to a HashTable; HttpContext instead makes directly use of a HashTable. What's essential here is to be aware of the scope of the stuff you place there.

kick it on DotNetKicks.com

Published 17 April 2007 04:18 AM by simoneb
Filed under:

Comments

# DotNetKicks.com said on 16 April, 2007 08:14 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Sleepy said on 19 April, 2007 01:59 PM
Uh...if there's more than one instance it's no longer the Singleton pattern. This is just an instance per page. Single means single. This seems more like delegated conditional lazy initialization or something - but it ain't a Singleton (or a derivative of the Singleton pattern). It's really nothing more than an instance variable on the page. RP
# simoneb said on 19 April, 2007 04:03 PM

Sleepy,

the singleton pattern is always dependent upon the context where it's applied. Most of times the singleton pattern is referred to as a single instance per AppDomain, but it's not always the case. Your class is not an absolute singleton, it's a singleton in you AppDomain, but in another AppDomain you could have another instance of the same class. In this case the context is the Page, and thus the name, singleton-per-page.

This site

Search

Go

This Blog

News

Syndication

Sponsors

  • MaximumASP