2012-01-10 60 views
7
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是否有效?MemoryStream是否在作爲ActionResult返回時自動處理?

編輯:

對於我而言,我發現,引入的「使用」語句不工作(拋出的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); 
+2

它總是一個好主意,與實現IDisposable,類打交道你是不是相信ASP.NET是要清理時,使用「使用」聲明在你之後。 – 2012-01-10 17:54:24

+1

什麼'文件(流,字符串)'做與流?通常創建流的對象也應該處理流。在這種情況下,您將負責處理該流。 – 2012-01-10 17:55:56

+0

@GeorgeStocker會調用Dispose()方法嗎?我想在調用'return'之後,它不會完成'using'塊。 – 2012-01-10 17:59:34

回答

19

是否返回它作爲一個ActionResult當一個MemoryStream都會自動處置?

是的,MVC(至少版本3)將爲您清理它。你可以把WriteFile方法的look at the sourceFileStreamResult

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響應。

您還可以通過創建,這是否一個虛擬的流驗證此行爲:

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

所以MVC 處置它。

+0

謝謝您的確切答案。 :) – 2012-01-10 18:24:24

+0

...有趣的是,如果我爲我的應用程序使用'使用'語句 - 我生成一個內部服務器錯誤。 – 2012-01-10 18:31:03

+0

@SeanAnderson你能夠獲得堆棧跟蹤和異常嗎?取決於流;它可能不喜歡被處置兩次。 – vcsjones 2012-01-10 18:50:53

-3

以下是處置流的有效代碼。如果封裝在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(); 
} 
+5

這不會從File對象下拉出流嗎? – 2012-01-10 18:03:45

+1

@ LasseV.Karlsen這也是我想知道的。如果它不......爲什麼不呢? :) – 2012-01-10 18:04:46

+0

+1評論@SeanAnderson我想看看關於這個我自己的文檔。 – Craig 2012-01-10 18:06:33

2

肖恩:不要使用'使用',因爲它會解決對象。讓MVC訪問一個Disposed對象。因此,您遇到的異常(服務器錯誤)肯定是ObjectDisposedException。之前發佈的WriteFile函數爲您分配對象。

2

在這種情況下,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); 
    } 
} 
+0

其實很不錯的解決方案!似乎它會保存1個緩衝區副本。 (可能取決於保持「圖表」活力的交易規模有多大) – 2013-11-11 16:36:32

相關問題