Blazorwasm: Using AddHttpClient with Singleton does not do what you think


.NET Core has three distinct ways it injects dependencies: scoped, transient and singleton. In this post I will concentrate on singleton with what is either a bug or a quirk of the framework that can cause problems if one isn't careful.

The problem

Blazor contains an extension method that can be run during the application startup that automatically injects HttpClient to whichever service is given as a type:


builder.Services.AddHttpClient<IService, Service>(...)

In this example code, Service instance at the point of injection provides HttpClient automatically. When doing this with Singleton type service however, we run into problems. It seems that when we use AddHttpClient method it overwrites any singleton services with either a transient or scoped service.

blazor-singleton-add-httpclient-demo project provides demonstration of this behaviour.

A solution

One solution to this behaviour is to use a factory service to provide the client and inject it through more traditional methods. You will need a total of three new files (potentially only two):

Here's the code for all three:


    public interface IhttpFactory
       HttpClient GetClient();


    public class HttpFactory : IhttpFactory
        private HttpOptions _options;
        private HttpClient _client;

        public HttpFactory(IOptions<HttpOptions> options)
            _options = options.Value;
            _client = new HttpClient
                BaseAddress = new Uri(_options.BaseUrl)

        public HttpClient GetClient()
            return _client;


    public class HttpOptions
        public string BaseUrl { get; set; }

Then in Program.cs we attach the factory to DI.


    public static async Task Main(string[] args)
        var builder = WebAssemblyHostBuilder.CreateDefault(args);

        builder.Services.AddSingleton<ICounterService, CounterService>();

        builder.Services.AddSingleton<IhttpFactory, HttpFactory>();
        builder.Services.Configure<HttpOptions>(options => options.BaseUrl = "http://localhost:5000");

        await builder.Build().RunAsync();

And finally in the service (in this case CounterService) we inject the factory and retrieve HttpClient.


public class CounterService : ICounterService
    private HttpClient _http;

    public CounterService(IhttpFactory httpFactory)
        _http = httpFactory.GetClient();