Extreme ASP.NET Makeover: Singleton - Overview

Download

Right click “Save as…”

  • Mid Quality WMV (Lo-band, Mobile)
  • MP3 (Audio only)
  • MP4 (iPhone, Android)

Extreme ASP.NET Makeover: Death of a Singleton-Overview

Let’s review one of the changes we made to the code in part 6. In that discussion, we refactored the authorization checking into a separate class, AuthorizationServices. To do that, we moved a lot of code from the Page_Load method of our Default.aspx page into it. Figure 1 shows an excerpt from the RetrieveCurrentStatusFor method.

Figure 1 Excerpt from the RetrieveCurrentStatusFor Method

return new CurrentCapabilitiesStatus
{
    CanView = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.ReadPage,
        currentUsername, currentGroups),
    CanDownloadAttachments = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.DownloadAttachments,
        currentUsername, currentGroups),
    CanSetPerms = AuthChecker.Instance.CheckActionForGlobals(
        Actions.ForGlobals.ManagePermissions,
        currentUsername, currentGroups),
    CanAdmin = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.ManagePage,
        currentUsername, currentGroups),
    CanViewDiscussion = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.ReadDiscussion,
        currentUsername, currentGroups),
    CanPostDiscussion = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.PostDiscussion,
        currentUsername, currentGroups),
    CanManageDiscussion = AuthChecker.Instance.CheckActionForPage(
        currentPage, Actions.ForPages.ManageDiscussion,
        currentUsername, currentGroups),
    CanEdit = canEdit,
    CanEditWithApproval = canEditWithApproval
};

Notice all the calls to AuthChecker.Instance. This is an example of the Singleton Design Pattern at work. We’ll define it now to get everyone on the same page.

There Can Be Only One

The defining characteristic of a singleton class is that there is only one instance of it at any given time (unless it is never used, in which case, there may not be any instances of it depending on how it is implemented).

Implementing a Singleton is not quite as trivial as it sounds. The intuitive approach is to make the constructor private and provide a public static accessor to it, as shown in Figure 2.

Figure 2 Implementing a Singleton

public class MySingleton
{
    private static MySingleton _instance;
 
    private MySingleton()
    {
    }
 
    public static MySingleton Instance
    {
        get
        {
            if (_instance == null)
                _instance = new MySingleton();
            return _instance;
        } 
 
    }
 
    ...
}

This works fine in single-threaded environments, but not in multi-threaded ones. Two threads could enter the Instance property before the instance variable is instantiated, resulting in two instances of the class. There are a few ways around this,  but the easiest solution in .NET takes advantage of the CLR and its guarantees around initialization of static variables, as shown in Figure 3.

Figure 3 Dealing With Two Instances of the Class

public class MySingleton
{
    private static readonly MySingleton _instance = new MySingleton();
 
    private MySingleton()
    {
    }
 

    public static MySingleton Instance
    {
        get
        {
            return _instance;
        } 
 
    }
 
    ...
}

The fact that the instance variable is marked static and readonly ensures only one instance can be created, even in multi-threaded environments.  Another side effect is that an instance of MySingleton is always going to be created for the _instance variable, regardless of if it’s needed or not.  This is, however, rarely an issue in real world code.

Some Background

A common implementation for singletons is to use double-checked locking, like so:

if (_instance == null) {
    lock (_initializationLock) {
        if (_instance == null) {
            _instance = new MySingleton();
        }                    
    }                
}
return _instance;

 

On the surface, double-checked locking appears to offer the best of all worlds. Singletons are instantiated lazily, are never instantiated if never used, and do not take an expensive lock after the singleton is initialized. Unfortunately, double-checked locking is subtlely broken in the vast majority of cases. You can read more about the problems in The “Double-Checked Locking is Broken” Declaration by David Bacon, et al. Double-checked locking can be fixed on the CLR (and JDK5+) by declaring the _instance variable with the volatile keyword. See Implementing Singleton in C# and Exploring the Singleton Design Pattern in the MSDN Library for more information.

That’s about as detailed as we’re going to get on implementing singletons, because there are a number of good resources already available. Plus, the focus of this article is to move away from them, not add more.

With that background behind us, the “singleton” classes in ScrewTurn aren’t actually singletons. Figure 4 looks at the AuthChecker class first.

Figure 4 AuthChecker Class

public class AuthChecker {
 
    private static AuthChecker instance;

    public static AuthChecker Instance 
    {
        get { return instance; }
        set { instance = value; }
    }
 
    private ISettingsStorageProviderV30 settingsProvider;
 
    public AuthChecker(ISettingsStorageProviderV30 settingsProvider) 
    {
        if(settingsProvider == null) throw new 
            ArgumentNullException("settingsProvider");
 
        this.settingsProvider = settingsProvider;
    }
 
    public bool CheckActionForGlobals(string action, 
        string currentUser, string[] groups) 
    {
   
    }
 
    public bool CheckActionForNamespace(NamespaceInfo nspace, 
        string action, string currentUser, 
        string[] groups) 
    {
   
    }
 
    public bool CheckActionForPage(PageInfo page, string action, 

    {
   
    }
 
    public bool CheckActionForDirectory(
        IFilesStorageProviderV30 provider, 
        string directory, string action, 
        string currentUser, string[] groups) 
    {
   
    }
}

The most obvious way you can tell this isn’t a singleton is that it has a public constructor. So the client code can create as many of these as it likes. Furthermore, the class doesn’t actually instantiate the instance variable itself. That is done elsewhere in the code, in StartupTools.cs, using the public constructor.

That said, it is still used as a singleton within the context of the application. Yes, the code could create more than one instance and re-assign the existing instance and basically treat it like any other class. But in the application’s current state, it doesn’t. This half-way status between singleton and non-singleton would be a bone of contention for us at a code review, so let’s see what we can do to fix it. But first, like any refactoring, we need to justify it.

Other videos from this article

Read the full article at http://msdn.microsoft.com/magazine/ee343987.aspx

Tags:

Follow the Discussion

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.