Search code examples
c#staticdotnet-httpclientusing

Is it viable/thread-safe to use a static HttpClient in a using block?


Say I have a controller with an Index() method, and this controller utilizes multiple "Manager classes" that manage certain assets that need to be retrieved with an HttpClient from an API.

I've read that sharing an HttpClient with multiple calls is better than to reinstantiate it with every call to save ports.

I do however want to dispose of the HttpClient before the controller returns the view, because the view contains an entire Knockout/Typescript based front end project that handles the rest of the data (so it's basically only settings and meta data stuff).

Do I need to pass the HttpClient variable to each and every "Manager class", or does it suffice to do something like the following, and use a static HttpClient inside the classes?

public ActionResult Index()
{
  using (Globals.Client = new System.Net.Http.HttpClient())
  {
    // do stuff like SettingManager.GetSetting("settingKey") which uses 
    // the Globals.Client variable
  }
  return View();
}

Or should I not even want to dispose the HttpClient in the first place?


Solution

  • One solution is to make a separate dependency responsible for managing your HttpClient. This has the side benefit of keeping your controllers from depending directly on HttpClient. Any class that depends on HttpClient becomes harder to test. It's also a maintenance issue because if you want to change the behavior you have to change it everywhere. Imagine if you decide one day that whatever you're getting from that HttpClient can be cached? You'd have to change it in lots of classes.

    You can define an abstraction and implementation like this:

    public interface IDoesSomething
    {
        string GetSetting(string key);
    }
    
    public class HttpClientDoesSomething : IDoesSomething, IDisposable
    {
        private readonly HttpClient _client;
        private readonly string _apiUrl;
    
        public HttpClientDoesSomething(string apiUrl)
        {
            _client = new HttpClient();
            _apiUrl = apiUrl;
        }
    
        public string GetSetting(string key)
        {
            // use the client to retrieve the setting
        }
    
        public void Dispose()
        {
            _client?.Dispose();
        }
    }
    

    Now the problem is moved out of your controller because you inject the interface:

    public class MyController : Controller
    {
        private readonly IDoesSomething _doesSomething;
    
        public MyController(IDoesSomething doesSomething)
        {
            _doesSomething = doesSomething;
        }
    
        public ActionResult Index()
        {
            var setting = _doesSomething.GetSetting("whatever"); 
            // whatever else this does.
            return View();
        }
    }
    

    Now in your startup configuration you can register HttpClientDoesSomething as a singleton:

    services.AddSingleton<IDoesSomething>(new HttpClientDoesSomething("url from settings"));
    

    Your implementation is disposable, so if you do need to create and dispose it you will also dispose the HttpClient. But it won't be an issue because your application will keep reusing the same one.