Could not load file or assembly 'System.Net.Http'!!! What the heck is happening?

Last week, one of my colleagues tried upgrading some of core libraries from .net framework 4.7 into .netstandard 2.0 and it got me 3 days to handle THIS shit:

"Could not load file or assembly 'System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)"

 

Some people says that this is because of an issue of .NET Framwork 4.7, some saying that the issue could be resolved by using Binding Redirect.

Well, Nothing works!!!.

So, I tried to read some articles about netstandard and what it can support.  And yay! I finally found out that it supports .NET Framework 4.6.1 and above. But seriously, why can't we refer a .net standard library such as System.Net.Http?

Well, after several hours of investigating and TALKING (yeah, you can say IT IS FIGHTING). We figured out that, .NET Framework 4.7 cannot use System.Net.Http directly. Instead, we have to create another core netstandard library which refer System.Net.Http instead, then write some class that wrap the HttpClient library as below:

public class Request
{
  private HttpClient _client { get; set; }

  private bool _usefaultCredentials = true;
  private List<string> _mediaTypeWithQualityHeaderValue = new List<string>
  {
    "application/json"
  };

  private string _authenticationType = "Basic";
  private string _credentials = string.Empty;

  public Request UseDefaultCredentials(bool useDefaultCredentials)
  {
    _usefaultCredentials = useDefaultCredentials;
    return this;
  }

  public Request SetAuthType (string type)
  { 
    _authenticationType = type;
    return this;
  }

  public Request SetCredentials (string cred)
  {
    _credentials = cred;
    return this;
  }

  public Request SetMediaTypeWithQualityHeader(params string[] mediaTypes)
  {
    _mediaTypeWithQualityHeaderValue = new List<string> (mediaTypes);
    return this;
  }

  private HttpClient CreateRequest()
  {
    var handler = new HttpClientHandler { UseDefaultCredentials = _usefaultCredentials };
    var client = new HttpClient(handler);
    client.DefaultRequestHeaders.Accept.Clear();
    foreach (var mediaType in _mediaTypeWithQualityHeaderValue)
    {
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
    }

    if (!string.IsNullOrWhiteSpace(_authenticationType) && !string.IsNullOrWhiteSpace(_credentials))
    {
      client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_authenticationType, _credentials);
    }

    return client;
  } 

  public async Task<T> PostAsync<T>(string uri, object @params)
  {
    using (var req = CreateRequest())
    {
      var body = JsonConvert.SerializeObject(@params);
      var content = new StringContent(body, Encoding.UTF8, "application/json");
      var res = req.PostAsync(new Uri(uri), content);
      var str = await res.Result.Content.ReadAsStringAsync();
      if (!res.Result.IsSuccessStatusCode)
      {
        throw new HttpResponseException(res.Result.StatusCode, res.Result.ReasonPhrase);
      }
    return JsonConvert.DeserializeObject<T>(str);
    }
  }

  public async Task<T> PutAsync<T>(string uri, object @params)
  {
    using (var req = CreateRequest())
    {
      var body = JsonConvert.SerializeObject(@params);
      var content = new StringContent(body, Encoding.UTF8, "application/json");
      var res = req.PutAsync(new Uri(uri), content);
      var str = await res.Result.Content.ReadAsStringAsync();
      if (!res.Result.IsSuccessStatusCode)
      {
        throw new HttpResponseException(res.Result.StatusCode, res.Result.ReasonPhrase);
      }
      return JsonConvert.DeserializeObject<T>(str);
    }
  }

  public async Task<T> GetAsync<T>(string uri)
  {
    using (var req = CreateRequest())
    {
      var response = req.GetAsync(new Uri(uri));
      var content = await response.Result.Content.ReadAsStringAsync();
      if (!response.Result.IsSuccessStatusCode)
      {
        throw new HttpResponseException(response.Result.StatusCode, response.Result.ReasonPhrase);
      }
      return JsonConvert.DeserializeObject<T>(content);
    }
  }

  public async Task DeleteAsync(string uri)
  {
    using (var req = CreateRequest())
    {
      var res = await req.DeleteAsync(new Uri(uri));
      if (!res.IsSuccessStatusCode)
      {
        throw new HttpResponseException(res.StatusCode, res.ReasonPhrase);
      }
    }
  }
}

Of course that the class does not cover all cases of HttpClient, and there are classes that we cannot extend because it is sealed class but don't worry, the fix will be applied after .NET Framework 4.7.2 or just use Microsoft.Net.Http instead.

I didn't have a change to take a look at Microsoft.Net.Http yet because we don't have enough time left to give a try. I would love to here if anyone successful refer Microsoft.Net.Http, I will definitely take a look at it after this release.

Thanks for reading this article. Happy Coding!!!

Alex Chau,