Удаляется ли MemoryStream автоматически при возврате его как ActionResult?

public ActionResult CustomChart(int reportID)
{
    Chart chart = new Chart();

    // Save the chart to a MemoryStream
    var imgStream = new MemoryStream();
    chart.SaveImage(imgStream);
    imgStream.Seek(0, SeekOrigin.Begin);

    // Return the contents of the Stream to the client
    return File(imgStream, "image/png");
}

Я привык использовать оператор using в сочетании с MemoryStreams. Это сценарий, в котором оператор using не требуется? Или допустимо вызывать return внутри оператора using?

РЕДАКТИРОВАТЬ:

Для моих целей я обнаружил, что введение оператора using НЕ работает (вызывает исключение ObjectDisposedException). Вот что я делаю на стороне клиента:

$('#ReportTest').bind('load', function () {
                        $('#LoadingPanel').hide();
                        $(this).unbind('load');
                    }).bind('error', function () {
                        $('#LoadingPanel').hide();
                        $(this).unbind('error');
                    }).attr('src', '../../Chart/CustomChart?ReportID=' + settings.id);

person Sean Anderson    schedule 10.01.2012    source источник
comment
Всегда полезно использовать оператор using при работе с классами, реализующими IDisposable, независимо от того, считаете ли вы, что ASP.NET очистит вас после вас.   -  person George Stocker    schedule 10.01.2012
comment
что File(stream, string) делает с потоком? обычно объект, создавший поток, также должен избавляться от потока. в этом случае вы будете ответственны за утилизацию потока.   -  person Jason Meckley    schedule 10.01.2012
comment
@GeorgeStocker, будет ли вообще вызван метод Dispose()? Я бы подумал, что после вызова return он не завершит блок using.   -  person    schedule 10.01.2012
comment
Возможный дублированный stackoverflow.com/questions/662773/   -  person Craig    schedule 10.01.2012
comment
@Shark Dispose вызывается в finally, так что да - он будет вызван.   -  person vcsjones    schedule 10.01.2012
comment
Создайте потомка Stream, который записывает сообщения с помощью Debug.WriteLine, чтобы проверить, что происходит.   -  person Lasse V. Karlsen    schedule 10.01.2012
comment
Это хорошая практика Dispose одноразовые предметы. Но для MemoryStream единственное, что подойдет, - это запретить вам читать / писать в него. Итак, ваша текущая реализация работает нормально.   -  person Magnus    schedule 10.01.2012


Ответы (4)


Удаляется ли MemoryStream автоматически при возврате его как ActionResult?

Да, MVC (по крайней мере, версия 3) очистит это за вас. Вы можете посмотреть источник метод WriteFile в FileStreamResult:

protected override void WriteFile(HttpResponseBase response) {
    // grab chunks of data and write to the output stream
    Stream outputStream = response.OutputStream;
    using (FileStream) {
        byte[] buffer = new byte[_bufferSize];

        while (true) {
            int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
            if (bytesRead == 0) {
                // no more data
                break;
            }

            outputStream.Write(buffer, 0, bytesRead);
        }
    }
}

Строка using (FileStream) { поместит Stream в блок using, таким образом избавившись от него, когда он записал содержимое в Http Response.

Вы также можете проверить это поведение, создав фиктивный поток, который делает это:

public class DummyStream : MemoryStream
{
    protected override void Dispose(bool disposing)
    {
        Trace.WriteLine("Do I get disposed?");
        base.Dispose(disposing);
    }
}

Таким образом, MVC избавится от него.

person vcsjones    schedule 10.01.2012
comment
Спасибо за исчерпывающий ответ. :) - person Sean Anderson; 10.01.2012
comment
... как ни странно, если я использую оператор using для своих приложений, я генерирую внутреннюю ошибку сервера. - person Sean Anderson; 10.01.2012
comment
@SeanAnderson Можете ли вы получить трассировку стека и исключение? В зависимости от потока; ему может не понравиться, когда его выбрасывают дважды. - person vcsjones; 10.01.2012
comment
Когда для страницы вызывается рендеринг, мне говорят, что поток недействителен. Ему просто не нравится, когда собирают сборщик мусора так рано. - person Sean Anderson; 11.01.2012
comment
С опозданием на несколько лет ... но я перенес принятый ответ. Извините за задержку! - person Sean Anderson; 12.04.2016

Шон: НЕ используйте «using», так как оно уничтожит объект. Оставляя MVC доступ к объекту Disposed. Следовательно, возникшее вами исключение (ошибка сервера), безусловно, является ObjectDisposedException. Ранее опубликованная функция WriteFile удаляет объект за вас.

person Steven Licht    schedule 11.01.2012

В этой ситуации MemoryStream не требуется. Вы можете избежать этого, создав Custom ActionResult следующим образом:

public class ChartResult : ActionResult
{
    private Chart _chart;

    public ChartResult(Chart chart)
    {
        if (chart == null)
            throw new ArgumentNullException("chart");
        _chart = chart;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = "image/png";
        response.BufferOutput = false;

        _chart.ImageType = ChartImageType.Png;
        _chart.SaveImage(response.OutputStream);
    }
}
person Kalten    schedule 29.08.2013
comment
На самом деле довольно красивое решение! Похоже, это тоже сохранит 1 буферную копию. (Это может зависеть от того, насколько важно поддерживать Chart в живых так долго) - person ; 11.11.2013

Ниже приведен действительный код, который удаляет поток. Если он заключен в блок using, метод MemoryStream.Dispose() будет вызываться автоматически при возврате.

public ActionResult CustomChart(int reportID)
{
    Chart chart = new Chart();

    using (var imgStream = new MemoryStream()) {
        chart.SaveImage(imgStream);
        imgStream.Seek(0, SeekOrigin.Begin);
        return File(imgStream, "image/png");
    }
}

Вы можете добиться того же результата, поместив объект в блок try и затем вызвав Dispose в блоке finally. Фактически, согласно документации MSDN, именно так оператор using транслируется компилятором. А в блоке try..finally finally всегда будет выполняться, даже если try завершится через return.

Компилятор преобразует блок using в следующее:

MemoryStream imgStream = new MemoryStream();

try 
{
    chart.SaveImage(imgStream);
    imgStream.Seek(0, SeekOrigin.Begin);
    return File(imgStream, "image/png");
}
finally
{
    if (imgStream != null) 
        ((IDisposable)imgStream).Dispose();
}
person Dennis Traub    schedule 10.01.2012
comment
Разве это не вырвет поток из-под объекта File? - person Lasse V. Karlsen; 10.01.2012
comment
@ LasseV.Karlsen Это то, что мне тоже интересно. А если нет ... почему нет? :) - person Sean Anderson; 10.01.2012
comment
+1 за комментарий @SeanAnderson Я сам хотел бы увидеть документацию по этому поводу. - person Craig; 10.01.2012
comment
@ LasseV.Karlsen Да, будет. - person Dennis Traub; 10.01.2012
comment
Если поток обрабатывается непосредственно в конструкторе File, проблем быть не должно. - person Magnus; 10.01.2012
comment
Файл здесь не конструктор, это метод контроллера. - person Lasse V. Karlsen; 10.01.2012
comment
@ LasseV.Karlsen, верно, все еще зависит от того, что на самом деле делает этот метод. - person Magnus; 10.01.2012
comment
Итак, мы установили, работает это или нет? Если этого не произойдет, я закрою этот вопрос как слишком локализованный. - person Lasse V. Karlsen; 10.01.2012
comment
Я только что попробовал в своем приложении. Введение оператора using НЕ работает для моего приложения. У меня возникла внутренняя ошибка сервера при попытке визуализации файла изображения. - person Sean Anderson; 10.01.2012
comment
Я не рекомендую этого делать: он закроет поток перед его возвратом, и вы получите ошибку. Предположим, вы возвращаете поток с PDF-файлом для отображения в диалоговом окне, вы получите сообщение об ошибке «Невозможно получить доступ к закрытому потоку», если поместите возврат в предложение using \ - person victor; 23.09.2014
comment
Просто вложил свои 0,02 доллара в то, как я это реализовал, думая, что так и должно быть, и это полностью не работает. Он действительно выдергивает поток из файла (..) до того, как сможет его использовать. Я погуглил, удалит ли файл поток? mvc для проверки (чтобы проверить то, что я подозревал после небольшого дополнительного тестирования), и это то, к чему меня привели. - person claudekennilol; 17.07.2015