amindomeniko
amindomeniko

Reputation: 429

How to dispose the data provider instance using HttpClient?

I have created my data provider using Repository Pattern.

First, I designed a base repository interface as following:

internal interface IGenericRepository<T, in TResourceIdentifier>
{
    Task<IEnumerable<T>> GetManyAsync();
    Task<T> GetAsync(TResourceIdentifier id);
    Task PutAsync(T model);
    Task<T> PostAsync(T model);
    Task DeleteAsync(TResourceIdentifier id);
}

Then I implemented it:

public class GenericRepository<T, TResourceIdentifier> : IDisposable, IGenericRepository<T, TResourceIdentifier> 
    where T : class
{
    private bool _disposed;
    protected HttpClientHelper<T, TResourceIdentifier> Client;

    protected GenericRepository(string addressSuffix)
    {
        Client = new HttpClientHelper<T, TResourceIdentifier>(Properties.Settings.Url, addressSuffix);
    }

    public async Task<IEnumerable<T>> GetManyAsync()
    {
        return await Client.GetManyAsync();
    }

    // All other CRUD methods and dispose

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if(_disposed || !disposing) return;

        if(Client != null)
        {
            var mc = Client;
            Client = null;
            mc.Dispose();
        }

        _disposed = true;
    }
}

Then I created custom repository interface for each of my entities. For example:

internal interface IOrderRepository : IGenericRepository<Order, int>
{
    Task<IEnumerable<Order>> GetOrderBySomeConditionAsync(string condition );

}

And finally, I implemented the custom repository:

public class OrderRepository : GenericRepository<Order, int>, IOrderRepository
{
    public OrderRepository(string addressSuffix) : base(addressSuffix)
    {
    }

    public async Task<IEnumerable<Order>> GetOrderBySomeConditionAsync(string condition)
    {
        //get all the orders (GetManyAsync()) and then returns the ones meeting the condition
    }
}

Note that HttpClientHelperuses HttpClient and needs to be disposed manually.

I have created a MVC web application and have defined the repositories at the class level as such:

IOrderRepository _orderRepository = new OrderRepository();

When I call _orderRepository in my CRUD operations, it does not hit dispose after its use. In order to fix that I have ended up implementing like this:

private async Task<IEnumerable<OrderViewModel>> GetOrders()
{
    using(var orderRepository = new OrderRepository())
         return await orderRepository.GetManyAsync();
}

This would hit the Dispose but is anti pattern.

What am I missing in my implementation that the instance is not disposed on each call?

Upvotes: 0

Views: 607

Answers (2)

SpiritBob
SpiritBob

Reputation: 2662

Writing a Dispose method in your generic repository does not mean it will be invoked automatically, whenever you feel like it should. It's intended to be invoked individually, hence why you must either use a using statement (as you have shown), or the Dispose method inside your code.

Alternatively, you can leave that job for the garbage collector.

You should also create a finalizer in your generic repository, if you're convinced on using GC.SuppressFinalize(this);

Read more about that here - When should I use GC.SuppressFinalize()?

As R Jain pointed out, you should also create a static class to hold your HttpClient. You'll have to use HttpResponseMessages for your needs, or HttpContent.

Some more resources to read:

  1. HttpClient single instance with different authentication headers
  2. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Upvotes: 1

R Jain
R Jain

Reputation: 598

You should not be disposing HTTPClient after every request.

[https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests][1]

As the above link says -

Therefore, HttpClient is intended to be instantiated once and reused throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. That issue will result in SocketException errors. Possible approaches to solve that problem are based on the creation of the HttpClient object as singleton or static, as explained in this Microsoft article on HttpClient usage.

Upvotes: 3

Related Questions