Friday 12 February 2010

Singleton pattern in ASP.NET applications

  
Some years ago I remember I spent quite some time while trying to debug a cross session contamination of data.

The problem ended up being due to an improper use of the static modifier in the context of ASP.NET applications.

In fact, in ASP.NET,  static objects are instantiated one time for each application domain, and all processing in the application share the same object, while at that time I understood that the static modifier acted within the page life span!

For this very reason, a Singleton pattern that relies on a static object will be most of the times useless if the wished behaviour is to have its life span limited to the page life cycle: a static object is always shared across the entire application!

In my own experience, when I require a single instance of an object I most often want its scope and life cycle to be within a web request or single session and in very few occasions to be application wide.

I decided I was going to create a helper class that would allow me to instantiate a Singleton instance of an object and being able to decide its scope.

I put together a Singleton helper class and 3 persister classes: one for the Session, one for the Page life cycle and one for the Application.

The persister classes would define the singleton life span and scope so that if I wanted to have a single instance of MyObjectClass with a life span of a single web request I could easily get an instance of it in the following way:

MyObjectClass o = Singleton<MyObjectClass, ItemsPersister>.GetInstance();

Likewise within the session life span:

MyObjectClass o = Singleton<MyObjectClass, SessionPersister>.GetInstance();

And application life span:

MyObjectClass o = Singleton<MyObjectClass, ApplicationPersister>.GetInstance();
The Singleton<T, S> has T which is the type of the class I want to have a single instance of, and S the type of the Persister that I wish to adopt.

Here is the singleton helper and the persisters full code:

    public class Singleton<T, S> : ISingleton
        where T : new()
        where S : IPersister, new()
    {
        public static T GetInstance()
        {
            var identifier = new T().GetType().Name;
            var persister = new S();
            var instance = (T)persister.Get(identifier);
            if (instance == null)
            {
                instance = new T();
                persister.Set(identifier, instance);
            }
            return (T)instance;
        }
    }

    public interface IPersister
    {
        object Get(string name);
        void Set(string name, object o);
        void Remove(string name);
    }

    public class SessionPersister : IPersister
    {
        private HttpContext _Context;

        public SessionPersister()
        {
            this._Context = HttpContext.Current;
        }

        public object Get(string name)
        {
            return this._Context.Session[name];
        }

        public void Set(string name, object o)
        {
            this._Context.Session.Add(name, o);
        }

        public void Remove(string name)
        {
            this._Context.Session.Remove(name);
        }
    }

    public class ItemsPersister : IPersister
    {
        private HttpContext _Context;

        public ItemsPersister()
        {
            this._Context = HttpContext.Current;
        }

        public object Get(string name)
        {
            return this._Context.Items[name];
        }

        public void Set(string name, object o)
        {
            this._Context.Items.Add(name, o);
        }

        public void Remove(string name)
        {
            this._Context.Items.Remove(name);
        }
    }

    public class ApplicationPersister : IPersister
    {
        private HttpContext _Context;

        public ApplicationPersister()
        {
            this._Context = HttpContext.Current;
        }
        public object Get(string name)
        {
            return this._Context.Application[name];
        }

        public void Set(string name, object o)
        {
            this._Context.Application.Add(name, o);
        }

        public void Remove(string name)
        {
            this._Context.Application.Remove(name);
        }
    }

Please note that my solution is forcing a bit the concept of Singleton pattern. This pattern generally has the class that we want a single instance of, responsible for generating its own instance; this class also has only private constructors to guarantee that an instance of it can be created only within itself.

My main aim was not to reinvent the Singleton pattern but to provide a simple way of creating a single instance of a class within a chosen life scope.

If you are not familiar with the Singleton pattern please refer to http://en.wikipedia.org/wiki/Singleton_pattern for a more in depth explanation of the Singleton pattern.

Thanks for reading.


No comments: