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".

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 ... :)

Related Articles

 | Pawel Gerr

If you are using Autofac in your ASP.NET Core application then I recommend to update Autofac to version 4.6.1. This bugfix release brought a change how child scope handle additional registrations so that some errors like just disappear. With additional registrations I mean the…

Read article
 | Pawel Gerr

After several years of using the same Dependency Injection (DI) framework like Autofac you may have a good understanding how your components, implementing the interface IDisposable, are going to be disposed. With the nuget package Microsoft.Extensions.DependencyInjection the new…

Read article
 | Boris Wilhelms

All code shown in this article is available in a Github repository. What about Microservices? Before we talk about monoliths, we should spend some time thinking about when and why a microservices architecture makes sense. There is the single-responsibility principle, which states…

Read article