MemoryStream удаляется автоматически при возврате в качестве ActionResult? - PullRequest
11 голосов
/ 10 января 2012
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. Является ли это сценарием, в котором выражение «использование» не является необходимым? Или допустимо вызывать return внутри оператора using?

EDIT:

Для моих целей я обнаружил, что введение оператора 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);

Ответы [ 4 ]

21 голосов
/ 10 января 2012

Утилизируется ли 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) { поместит поток в блок использования, таким образом уничтожаяоб этом, когда он записал содержимое в Http Response.

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

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

Так что MVC будет распоряжаться им.

2 голосов
/ 29 августа 2013

MemoryStream не требуется в этой ситуации.Вы можете избежать этого, создав пользовательский 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);
    }
}
2 голосов
/ 11 января 2012

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

0 голосов
/ 10 января 2012

Ниже приведен действительный код, который удаляет поток.Если заключено в блок 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 в окончательном блоке.На самом деле, согласно документации 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();
}
...