简体   繁体   English

如何使用 HttpClient 处理数据提供者实例?

[英]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 HttpClientHelper uses HttpClient and needs to be disposed manually.注意HttpClientHelper使用HttpClient ,需要手动处理。

I have created a MVC web application and have defined the repositories at the class level as such:我创建了一个 MVC web 应用程序,并在 class 级别定义了存储库,如下所示:

IOrderRepository _orderRepository = new OrderRepository();

When I call _orderRepository in my CRUD operations, it does not hit dispose after its use.当我在 CRUD 操作中调用_orderRepository时,它在使用后不会命中 dispose。 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.这会击中 Dispose 但是反模式。

What am I missing in my implementation that the instance is not disposed on each call?我在我的实现中缺少什么实例没有在每次调用时处理?

You should not be disposing HTTPClient after every request.您不应该在每次请求后处理 HTTPClient。

[ https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests][1] [ https://docs.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.因此,HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重用。 Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads.为每个请求实例化一个 HttpClient class 将耗尽重负载下可用的 sockets 的数量。 That issue will result in SocketException errors.该问题将导致 SocketException 错误。 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.解决该问题的可能方法是基于将 HttpClient object 创建为 singleton 或 static,如这篇关于 HttpClient 使用的 Microsoft 文章中所述。

Writing a Dispose method in your generic repository does not mean it will be invoked automatically, whenever you feel like it should.在您的通用存储库中编写 Dispose 方法并不意味着它会在您觉得应该时自动调用。 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.它旨在单独调用,因此您必须使用using语句(如您所示)或代码中的Dispose方法。

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);如果您确信使用GC.SuppressFinalize(this);

Read more about that here - When should I use GC.SuppressFinalize()?在此处阅读更多相关信息 - 我应该何时使用 GC.SuppressFinalize()?

As R Jain pointed out, you should also create a static class to hold your HttpClient.正如R Jain指出的那样,您还应该创建一个 static class 来保存您的 HttpClient。 You'll have to use HttpResponseMessages for your needs, or HttpContent.您必须根据需要使用 HttpResponseMessages 或 HttpContent。

Some more resources to read:更多阅读资源:

  1. HttpClient single instance with different authentication headers 具有不同身份验证标头的 HttpClient 单个实例
  2. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM