ASP.NET Core Web API Performance - Throughput for Upload and Download
ASP.NET Core in production: Take back control of your web app

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

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

 

 

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

Daniel Crabtree

Great catch. I've never needed to register any dependencies in WebHostBuilder, but this is the kind of thing that can easily trip you up.

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Your Information

(Name and email address are required. Email address will not be displayed with the comment.)