Implementing the Singleton Pattern in ASP.NET Core

The Singleton Pattern is one of the most popular design patterns in software development. In large part, this is due to two key points. First, it is one of the patterns discussed in the very widely known book Design Patterns written by the “Gang of Four”. Second, it is the simplest and easiest to understand. Whenever I do an interview and ask a candidate which design patterns they are familiar with, the Singleton Pattern is typically the first mentioned.

The singleton refers to a pattern in which only a single instance of a class can be created. In order to achieve this, construction and initialization of the class are the responsibility of the class itself. By isolating the construction of the object, we can restrict it from other areas of the application. A class that is written in this way is commonly referred to as a “Singleton”.

This pattern has been around for a long time however, with ASP.NET Core there is a much simpler method for implementing a singleton. First, we will walk through how singletons can be implemented without depending on an IOC container and discuss the downsides. We will then take a look at how the same class can be implemented by harnessing the power of the Microsoft Dependency Injection framework in an ASP.NET Core application.

The Classic Approach

For many years, there has been a clearly defined process for creating and interacting with singletons. To get started, lets take a look at the most basic example.

public class Singleton
{
    private static readonly Singleton instance = new Singleton();

    private Singleton()
    {
    }

    public static Singleton Instance => instance;
}

In the above example, note the private constructor. This prevents an instance from getting created outside of the Singleton class. Rather, the singleton object can be obtained by accessing the Instance property, a common naming convention.

One downfall of this approach is we have no control over when the instantiation happens. The static instance field gets triggered at some point after the program starts and before it is used. If we want more control over when the instantiation happens we can slightly update our implementation.

Using the Lazy<Singleton> type, we can delay the instantiation of the Singleton class until the moment is it used for the first time. This is a little better but, there are still quite a few issues with this approach.

public class Singleton
{
    private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());

    private Singleton()
    {
    } 

    public static Singleton Instance => lazy.Value; 
}

The Problem

The “Classic Approach” works and has been used in applications for many years. That said, it does not come without its pitfalls. Below are the biggest gripes I have with both methods described above.

First, the class needs to be explicitly written as a singleton. This isn’t a huge concern for new classes however, if you have an existing class that you want to refactor, you must rewrite much of the class as well as address all of its usages.

Second, it is not unit test friendly. Relying on static fields makes it impossible (if not very difficult) to mock. This restricts the types of unit tests that can be written for any class that uses a class written in this way.

Lastly with construction handled inside the class, dependencies cannot be easily injected. This again makes unit testing difficult. Logic that would normally be mocked such as data access, file system interactions, etc must use the concrete implementation. As you can guess, this is a big no-no in the unit testing realm.

Singletons in ASP.NET Core

As I eluded to earlier there is a much better way to implement a singleton in ASP.NET Core. Most IOC containers allow a “lifetime” to be applied when wiring up dependencies. With the ASP.NET Core Dependency Injection framework, the following life cycles are available.

  • Transient – Each time a transient object is requested, a new instance will be created
  • Scoped – The same object will be used when requested within the same request
  • Singleton – The same object will always be used across all requests

Continuing with our previous example, lets incorporate our singleton class into the default ASP.NET Core project template. As shown below, we can create our class as a singleton by registering it with the AddSingleton function.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        services.AddSingleton<ISingleton, Singleton>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }
}

Now looking at our class, we no longer require any special logic to make it a singleton. Instead, we let the dependency injection framework do all the heavy lifting. Furthermore since the class is managed by an IOC container, our singleton can now have dependencies of its own injected into it.

public class Singleton : ISingleton
{
    private readonly IDataRepository _repository;

    public Singleton(IDataRepository repository)
    {
        _repository = repository;
    }
}

Consumers of the class can use the singleton by injecting it with the IOC container. They don’t even have to be aware of its life cycle!

 [Route("api/[controller]")]
 [ApiController]
 public class ValuesController : ControllerBase
 {
     private readonly ISingleton _singleton;

     public ValuesController(ISingleton singleton)
     {
         _singleton = singleton;
     }

     // Removed for brevity
}

Wrapping Up

The singleton pattern is just as relevant as the day the “Gang of Four” wrote the Design Patterns book. That said, implementing it in the traditional way has its downfalls and in most circumstances should be avoided. In an ASP.NET Core application, the Microsoft Dependency Injection framework (or other IOC container) should be used to manage class lifecycles.