ASP.NET Core – Beware – Singleton May Not Be Singleton

If you register a type as a singleton then you expect just 1 instance of this type in your whole application. What you may not know is that ASP.NET Core is creating 2 instances of IServiceProvider during building of the IWebHost that may lead to 2 instance of your "singleton".

In this article:

ASP.NET Core – Beware – Singleton May Not Be Singleton
Pawel Gerr is architect consultant at Thinktecture. He focuses on backends with .NET Core and knows Entity Framework inside out.
This is the case if you register a type, say MySingleton, when configuring the web host …
				
					WebHost
 .CreateDefaultBuilder()
 .UseStartup<Startup>()
 .ConfigureServices(services => services.AddSingleton<MySingleton>())
 .Build()
 .Run();
				
			

 …, e.g. so that is available in the constructor of your Startup

				
					public class Startup
{
  private readonly MySingleton _mySingletonFromHostingServiceProvider;

  public Startup(MySingleton mySingletonFromHostingServiceProvider)
  {
    _mySingletonFromHostingServiceProvider = mySingletonFromHostingServiceProvider;
  } 
  ...
}
				
			

Now, if we resolve MySingleton during normal web request we get a whole new instance instead the same instance as in constructor of the Startup class. 

				
					public void Configure(IApplicationBuilder app)
{
  app.Use((ctx, next) =>
  {
    var mySingleton = ctx.RequestServices.GetRequiredService<MySingleton>();
    
    // the comparison of 2 instances yields "false"
    var areEqual = _mySingletonFromHostingServiceProvider == mySingleton;

    Console.WriteLine($"==> {nameof(_mySingletonFromHostingServiceProvider)} == {nameof(mySingleton)}: {areEqual}");
         return next();
    });
}
				
			

 There are at least two ways to fix this problem.

Either pass an instance of MySingleton to method AddSingleton instead of passing just the type

				
					var mySingleton = new MySingleton();
WebHost
 .CreateDefaultBuilder()
 .UseStartup<Startup>()
 .ConfigureServices(services => services.AddSingleton(mySingleton))
 .Build()
 .Run();
				
			

 or by replacing the previous registration with a new one in ConfigureServices 

				
					public class Startup
{
  private readonly MySingleton _mySingletonFromHostingServiceProvider;

  public Startup(MySingleton mySingletonFromHostingServiceProvider)
  {
     _mySingletonFromHostingServiceProvider = mySingletonFromHostingServiceProvider;
  }

  public void ConfigureServices(IServiceCollection services)
  {
    services.Replace(new ServiceDescriptor(typeof(MySingleton), _mySingletonFromHostingServiceProvider));
    // alternative way 
    //services.AddSingleton(_mySingletonFromHostingServiceProvider);
 }
  ...
}
				
			

According to @davidfowl the ASP.NET team will address this problem in the future. 

PS: There is at least another one solution to fix this problem and gaining back the control over your web app but that’s for another time … 🙂

Free
Newsletter

Current articles, screencasts and interviews by our experts

Don’t miss any content on Angular, .NET Core, Blazor, Azure, and Kubernetes and sign up for our free monthly dev newsletter.

Related Articles
.NET
Incremental Roslyn Source Generators in .NET 6: Adapt Code Generation Based on Project Dependencies – Part 5
The Roslyn Source Generator, implemented in the previous articles of the series, emits some C# code without looking at the dependencies of the current .NET (Core) project. In this article our DemoSourceGenerator should implement a JsonConverter, but only if the corresponding library (e.g. Newtonsoft.Json) is referenced by the project.
08.07.2022
Angular
Configuring Lazy Loaded Angular Modules
Making our Angular modules configurable is an important step in building a reusable architecture. Having used Angular for a while you might be familiar with the commonly used forRoot() and forChild() functions, that some modules provide you with. But what is the best way to provide configuration in these cases?
16.06.2022
Angular
Master Web Component Forms Integration – with Lit and Angular
When a company has cross-framework teams, it is a good choice to use Web Components to build a unified and framework-independent component library. However, some pitfalls are to consider when integrating these components into web forms. Therefore, for a better understanding, we will look at two possible approaches and try to integrate them into an Angular form as an example.

Notice: All code samples are available on Github!
09.06.2022