Search code examples
c#asp.netdependency-injection.net-framework-versionioptions

How to inject IOption<T> into class using IWebHost builder's AddHttpClient() without UseStartup()


I am trying inject my IOptions into a class that requires a HttpClient and IOptions.

public class MyHttpService : IMyHttpService 
{
   private readonly HttpClient _httpClient;
   private readonly IOptions<MyOption> _myOption;

   MyHttpService(HttpClient httpClient, IOptions<MyOption> myOption)
   {
      _httpClient = httpClient;
      _myOption = myOption;
   } 

   //Further methods be here...
}

public class MyOption
{
   public string MyStringSetting {get;set;}
   //More settings be here...
}

Inside appsettings.json

{
   "MyOptions":{
     "MyStringSetting": "SomeSetting"
   }
}

Inside Program.cs

IWebHostBuilder builder = 
WebHost.CreateDefaultBuilder(args)
.Configure()
.ConfigureLogging()
.UseNLog()
.ConfigureServices((context, services) =>
{
   services.AddOptions();
   IConfiguration configuration = context.Configuration;
   services.Configure<MyOption>(configuration.GetSection("MyOptions"));
   services.AddHttpClient<IMyHttpService,MyHttpService>((provider,client) =>
   {
     client.BaseAddress = new Uri(\*api url*\);
   });
})

IWebHost webHost = builder.build();
webHost.Run();

When investigating the MyHttpService, it was not injecting the the IOptions. What am I missing?

I was expecting that the ConfigureServices() would resolve the IOptions dependency and inject the implemented MyOptions.


Solution

  • --edited for clarity--

    The IOption<MyOption> dependency was actually injected.

    The MyHttpService class which was using Options<MyOption> implementation which needed configuration and injection.

    Inside of the MyHttpService class there was a method overload that ignored the Options<MyOption>.

    public class MyHttpService : IMyHttpService 
    {
       private readonly HttpClient _httpClient;
       private readonly IOptions<MyOption> _myOption;
    
       MyHttpService(HttpClient httpClient, IOptions<**SomeOtherOption**> myOption)
       {
          _httpClient = httpClient;
          _myOption = myOption;
       } 
    
       ////Implemented Methods (that were overloaded!!!)
       public void Method(){/*does something with IOptions*/}
       public void Method(IOptions<T>){/*Method() overloaded with IOptions<T>*/}
    }
    
    public class MyOption
    {
       public string MyStringSetting {get;set;}
       //More settings be here...
    }
    
    public class SomeOtherOption
    {
       public string MyStringSetting {get;set;}
       //More settings be here...
    }
    

    Inside appsettings.json

    {
       "MyOptions":{
         "MyStringSetting": "SomeSetting"
       },
       "SomeOtherOptions":{  //<--This was missing
         "MyStringSetting": "SomeSetting"
       }
    }
    

    Inside Program.cs

    IWebHostBuilder builder = 
    WebHost.CreateDefaultBuilder(args)
    .Configure()
    .ConfigureLogging()
    .UseNLog()
    .ConfigureServices((context, services) =>
    {
       services.AddOptions();
       IConfiguration configuration = context.Configuration;
       services.Configure<MyOption>(configuration.GetSection("MyOptions"));
       services.Configure<SomeOtherOption>(configuration.GetSection("SomeOtherOptions"));
       services.AddHttpClient<IMyHttpService,MyHttpService>((provider,client) =>
       {
         client.BaseAddress = new Uri(MyOption);
       });
    
       ///Other Services that required **IMyHttpService** but used **SomeOtherOption** instead!!!
    })
    
    IWebHost webHost = builder.build();
    webHost.Run();
    

    TLDR; IOptions<T> DI was a red herring. There was an overloaded method that ignored the DI, and disguised the actual problem.