Search code examples
c#dependency-injection.net-coreasp.net-core-2.0httpclientfactory

Adding HttpClientFactory causing errors with cancellationTokenSource


I have a problem with the HttpClientFactory, i am trying to inject a CancellationTokenSource from DI to my "SomeClient" that configured to be like a:

services.AddHttpClient<ISomeClient, SomeClient>(a =>
                a.BaseAddress = new Uri(address))

and i am injecting cancellationTokenSource in Startup.cs in the AddScoped<>().

If i add CancellationTokenSource to SomeClient constructor it will say

Cannot resolve scoped service 'System.Threading.CancellationTokenSource' from root provider.

but if i create something like:

services.AddScoped<ISomeClient, SomeClient>();

and make a new local HttpClient in the constructor, and inject CancellationTokenSource, everything will be fine.

So my question is how to use CancellationTokenSource with HttpClientFactory?


Solution

  • For AddHttpClient, it will register SomeClient as Transient. But you register CancellationTokenSource as Scoped. This is the root caused.

    HttpClientFactoryServiceCollectionExtensions.cs

        public static IHttpClientBuilder AddHttpClient<TClient>(this IServiceCollection services)
            where TClient : class
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
    
            AddHttpClient(services);
    
            var name = TypeNameHelper.GetTypeDisplayName(typeof(TClient), fullName: false);
            var builder = new DefaultHttpClientBuilder(services, name);
            builder.AddTypedClient<TClient>();
            return builder;
        }
    

    HttpClientBuilderExtensions

            public static IHttpClientBuilder AddTypedClient<TClient>(this IHttpClientBuilder builder)
            where TClient : class
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
    
            builder.Services.AddTransient<TClient>(s =>
            {
                var httpClientFactory = s.GetRequiredService<IHttpClientFactory>();
                var httpClient = httpClientFactory.CreateClient(builder.Name);
    
                var typedClientFactory = s.GetRequiredService<ITypedHttpClientFactory<TClient>>();
                return typedClientFactory.CreateClient(httpClient);
            });
    
            return builder;
        }
    

    So, you could try register CancellationTokenSource as Transient.

    services.AddTransient<CancellationTokenSource>();