When to use Singleton

I’ve been using Dependency Injection (DI) engines for a while now and I’m always poking my Team Mates to configure the scope of the dependencies to most appropriate setting for each given case.

By using Singleton scoped dependencies, it’s ensured that the memory needed for the application to run is not duplicated among several instances of a class.

As such, singletons should be used for:

  • Stateless classes: classes that do not store any state on it’s internal variables;
  • Global single instance: Instead of using static classes and methods, the singleton scope ensures a controlled initialization of the class. An example of this condition is In-Memory Cache technique.
  • Shared access to a single resource: if there is some resource that is unique and it’s access is controlled by a class, than this class should be a singleton and should be responsible to manage the concurrency for accessing the resource.

.NET Core Singleton Scope

The .NET Core framework offers a simple Dependency Injection engine called ServiceCollection that supports several scopes by default:

  • AddTransient() will create an instance each time it is requested;
  • AddScoped() will create an instance for each request (e.g. a single instance for each HTTP request);
  • AddSingleton() will create an instance of the class upon the first request and reuse that instance among subsequent requests.

Example:

Let«’s consider an application that has an ImageStoreclass that loads images from a given location and keeps them in memory for future use.

Now, suppose this ImageStore is referenced by two different classes say ServiceA and ServiceB.

By configuring the DI engine to scopeImageStore as a Singleton, it will ensure that i) the same instance is reused among all the dependencies that use ImageStore and ii) the images stored on imageMap will not be duplicated (or n-uplicated) among several instances of ImageStore.

Now, to see the difference, consider the following .NET Core code:

This will create two instances of ImageStore, one for each request to GetService<ImageStore> and, thus, allocating 12MB (2 x 10 x 601KB) in the heap.

By changing the scope to Singleton as in:

The ImageStore is created and initialized only once and the same instance will be returned for every call to GetService<ImageStore> which will save the duplication on the heap: