Удаление HttpResponseMessage и его содержимого при создании исключения HttpResponseException внутри метода действия - PullRequest
0 голосов
/ 22 мая 2018

Источником моего вопроса является следующий код, который является частью фрагмента кода, содержащегося в документации Microsoft для обработки исключений в веб-интерфейсе asp.net:

var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
    Content = new StringContent(string.Format("No product with ID = {0}", id)),
    ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);

И HttpResponseMessage, и StringContent реализуют интерфейс IDisposable, но никто из приведенного выше кода не вызывает метод IDisposable.Dispose.
Это проблема?Есть ли побочный эффект, связанный с не выбрасыванием этих предметов?

Согласно этой статье возможное решение может заключаться в изменении приведенного выше кода следующим образом:

var content = new StringContent(string.Format("No product with ID = {0}", id));
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
    Content = content,
    ReasonPhrase = "Product ID Not Found"
};

this.Request.RegisterForDispose(content);
this.Request.RegisterForDispose(resp);

throw new HttpResponseException(resp);

Действительно ли это необходимо, или можно этого избежать?(в соответствии с тем, что указано в документации Microsoft)?

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Ответ, заключенный в HttpResponseException, будет утилизироваться средой asp.net, как и любой другой ответ, возвращаемый вами в результате действия.Вы можете легко проверить это самостоятельно, создав фиктивное ответное сообщение:

class DummyResponse : HttpResponseMessage {
    public DummyResponse(HttpStatusCode statusCode) : base(statusCode) {
    }

    protected override void Dispose(bool disposing) {
        Console.WriteLine("dispose called");
        base.Dispose(disposing);
    }
}

Затем бросьте HttpResponseException с этим ответом и установите точку останова в переопределении Dispose.Вы заметите, что вызывается Dispose, и если вы посмотрите на стек вызовов, вы увидите, что HttpControllerHandler отвечает за это (в контроллере веб-API asp.net).

Обратите внимание, что это исключениеперехватывается ApiControllerActionInvoker, классом, ответственным за вызов действий вашего контроллера API.Затем он просто захватывает yourException.Response и проталкивает его вперед по конвейеру, поэтому создание этого исключения ничем не отличается от простого возврата соответствующего ответа от действия вашего контроллера API.Должно быть ясно, я думаю, что структура будет распоряжаться всеми этими ответами, когда это будет сделано с ними.В противном случае это был бы довольно плохой дизайн.

Так что, не загромождайте свой код этими RegisterForDispose и позвольте фреймворку справиться с этим за вас.

0 голосов
/ 22 мая 2018

Проверка источника Microsoft для HttpResponseMessage.CS :

protected virtual void Dispose(bool disposing)
{
    // The reason for this type to implement IDisposable is that it contains instances of 
    // types that implement IDisposable (content). 
    if (disposing && !_disposed)
    {
        _disposed = true;
        if (_content != null)
        {
            _content.Dispose();
        }
    }
}

content имеет тип HttpContent.Проверка Microsoft Source для HttpContent.cs :

protected override void Dispose(bool disposing)
{
    Debug.Assert(_buffer != null);

    ArrayPool<byte>.Shared.Return(_buffer);
    _buffer = null;

    base.Dispose(disposing);
}

Комментарии для ArrayPool говорят:

/// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
/// in situations where arrays are created and destroyed frequently, resulting in significant
/// memory pressure on the garbage collector.

Изучение источника для ArrayPoolдает этот прекрасный драгоценный камень:

    /// <summary>
    /// Retrieves a shared <see cref="ArrayPool{T}"/> instance.
    /// </summary>
    /// <remarks>
    /// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
    /// that's intended for general applicability.  It maintains arrays of multiple sizes, and 
    /// may hand back a larger array than was actually requested, but will never hand back a smaller 
    /// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an 
    /// existing buffer being taken from the pool if an appropriate buffer is available or in a new 
    /// buffer being allocated if one is not available.
    /// </remarks>
    public static ArrayPool<T> Shared
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); }
    }

ArrayPool не использует WeakReference s или какой-либо подобный механизм для обеспечения надлежащей утилизации.Если вы арендуете буфер у ArrayPool.Shared, вам придется его вернуть, иначе вы вызовете утечку памяти.

Так что да, я бы сказал, что соблюдение IDisposable здесь очень важно.

...