0

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?

7
  • Don't do that, you will likely get an object disposed exception when another request is in-flight. You do not need to dispose it, it doesn't matter what the view contains. Commented May 15, 2019 at 14:45
  • @Crowcoder Won't the connections fill up with TCP ESTABLISHED that don't disappear until they time out? I've got quite a load so this could be a problem. Commented May 15, 2019 at 14:56
  • If you're creating something and disposing it within a method then it should be declared within that method. But we also shouldn't create/dispose HttpClient. We should create a single instance and re-use it. It's counter-intuitive because IDisposable means we dispose when we're done with it. But if we re-use it then we're never done with it. Commented May 15, 2019 at 14:58
  • I'm not an expert on the specifics at that level, but I know that it will re-use connections which saves from establishing handshakes every time and prevents socket exhaustion. Commented May 15, 2019 at 14:59
  • 1
    One suggestion is to define some abstraction (interface) that describes what you're using the HttpClient for, and inject that into your controller. The implementation of that class uses HttpClient. You can register it with DI as a singleton, and that class maintains a single instance of HttpClient. That kills three birds with one stone: Each individual controller isn't responsible for maintaining the lifetime of the client, there's no "global" client, and the controllers don't depend directly on HttpClient so they're more testable. Commented May 15, 2019 at 15:04

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.