Jordy Baylac
Jordy Baylac

Reputation: 550

How to configure other dependencies when using AddHttpClient

I am using AddHttpClient for injecting an HttpClient (using Typed clients) according to best practices in Microsoft documentation, but this approach does not allow me to configure the rest of the dependencies of my Service.

Let's say I have this setup:

    public interface ISerializer { /*...*/ }

    public class Serializer : ISerializer { 

        private readonly string _options;

        public Serializer(string options) 
        {
            _options = options;
        }
        
        /*...*/
    }

    public class ServiceA
    {
        private readonly HttpClient _client;
        private readonly ISerializer _serializer;

        public ServiceA(HttpClient httpClient, ISerializer serializer)
        {
            _client = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
            _serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
        }

        public string MakeCall()
        {
            // var body = _serializer.Serialize();
            return _client.GetAsync("").Result.StatusCode.ToString();
        }
    }

    public class ServiceB { 
        /*
          Similar to ServiceA
        */ 
    }

    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddServiceAandB(this IServiceCollection services)
        {            
            /* 
               If I do this below, Serializer("option 1") will be used in Services A and B
               however, what I would like is to have different Serializer's configurations for A and B (see full question below) 
            */
            services.AddTransient<ISerializer>((p) => new Serializer("option 1"));
            
            services.AddHttpClient<ServiceA>(c =>
            {
                c.BaseAddress = new Uri("api-url-for-A");
                c.Timeout = TimeSpan.FromSeconds(5);
            });

            services.AddHttpClient<ServiceB>(c =>
            {
                c.BaseAddress = new Uri("api-url-for-B");
                c.Timeout = TimeSpan.FromSeconds(5);
            });

            return services;
        }
    }

Question: How to configure ISerializer dependency with Serializer("option 1") for ServiceA and Serializer("option 2") for ServiceB? However, because I am using AddHttpClient and it is in charge of constructing ServiceA and ServiceB itself, I am not able to configure it as I want.

NOTE: As suggested, creating multiple implementations may works for simple cases, but imagine that Serializer receive an Options object and that creating different implementations for all options combinations would be impossible. That's why I would like to configure the options during DI setup.

Any help will be appreciated.

-------- UPDATE 1 --------

I do not intend to inject two ISerializer, but rather be able to configure Serializer and while constructing ServiceA and ServiceB and take advantage of .AddHttpClient.

For example (below code is not possible, but ideal):

services.AddHttpClient<ServiceA>(c =>
{
    c.BaseAddress = new Uri("api-url-for-A");
    c.Timeout = TimeSpan.FromSeconds(5);
});
services.AddTransient<ServiceA>(p => {
    return new ServiceA(p.GetHttpClientFor<ServiceA>(), new Serializer("option 1"));
});

services.AddHttpClient<ServiceB>(c =>
{
    c.BaseAddress = new Uri("api-url-for-B");
    c.Timeout = TimeSpan.FromSeconds(5);
});
services.AddTransient<ServiceB>(p => {
    return new ServiceB(p.GetHttpClientFor<ServiceB>(), new Serializer("option 2"));
});

// neither IServiceProvider.GetHttpClient method exists, nor I can use .AddHttpClient with AddTransient for same dependency.

Upvotes: 1

Views: 1136

Answers (1)

Fabio
Fabio

Reputation: 32445

In case of multiple options for serializer, introduce a class which will build new serializer based on required option.

public class SerializerBuilder
{
    private string _option;

    public SerializerBuilder With(string option)
    {
        _option = option;
    }

    public ISerializer Create()
    {
        return new Serializer(_option);
    }
}

public class ServiceA
{
    public ServiceA(HttpClient client, SerializerBuilder builder)
    {
        _serializer = builder.With("option A").Create();
    }
}

Upvotes: 1

Related Questions