2

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. 
4
  • Introduce different implementation types for ISerializer and so different services can use different options. Commented Feb 5, 2021 at 20:25
  • It won't work. you can't inject more than one ISerializer in to your pipeline. Otherwise how will the framework know which one to inject in to the service that wants it. Commented Feb 5, 2021 at 20:28
  • @Fabio my case is an example, but imagine that Serializer receive different options for how to serialize (indent, do not indent, use camel case, use sneak case), creating multiple implementations would be killing. Commented Feb 5, 2021 at 20:44
  • @Andy, I have added UPDATE 1 for clarifying your point. Commented Feb 5, 2021 at 20:45

1 Answer 1

1

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(); } } 
Sign up to request clarification or add additional context in comments.

3 Comments

ok, I like this. It will need to be Transient to be thread safe, but is fine, thanks
You can make it singleton if you can pass all required options to the Create method, then it would be more "factory" than a builder ;)
this is the best alternative so far, however, it brings construction concerns and coupling to the ServiceA, which I was trying to avoid (remember, I had ISerializer as dependency)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.