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

时间:2019-09-20 15:06:33

标签: c# asp.net-mvc repository-pattern dispose

我已经使用存储库模式创建了数据提供程序。

首先,我设计了一个基本的存储库接口,如下所示:

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);
}

然后我实现了它:

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;
    }
}

然后,我为每个实体创建了自定义存储库界面。例如:

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

}

最后,我实现了自定义存储库:

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
    }
}

请注意,HttpClientHelper使用HttpClient,需要手动处理。

我已经创建了MVC Web应用程序,并在类级别定义了存储库,如下所示:

IOrderRepository _orderRepository = new OrderRepository();

当我在CRUD操作中调用_orderRepository时,它在使用后不会碰到处置。为了解决这个问题,我最终实现了这样的方法:

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

这将达到Dispose,但是是反模式。

在实现过程中我遗漏了哪些实例没有在每次调用中都被处置?

2 个答案:

答案 0 :(得分:3)

您不应在每次请求后都丢弃HTTPClient。

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

如上面的链接所述-

  

因此,HttpClient旨在实例化一次并重新使用   在应用程序的整个生命周期中。实例化HttpClient   每个请求的类将耗尽可用的套接字数量   在重负载下。该问题将导致SocketException错误。   解决该问题的可能方法基于创建   HttpClient对象是单例还是静态,如本节所述   Microsoft关于HttpClient使用情况的文章。

答案 1 :(得分:1)

在通用存储库中编写Dispose方法并不意味着将在需要时自动调用它。它打算单独调用,因此,为什么您必须要么使用using语句(如您所示),要么在代码内使用Dispose方法。

或者,您可以将该工作留给垃圾收集器。

如果您确信使用GC.SuppressFinalize(this);

,则还应该在通用存储库中创建一个终结器。

在此处详细了解-When should I use GC.SuppressFinalize()?

正如R Jain所指出的,您还应该创建一个静态类来保存HttpClient。您必须根据需要使用HttpResponseMessages或HttpContent。

更多资源可供阅读:

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