2012-01-10 9 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"); 
} 

Tôi quen với việc sử dụng câu lệnh 'sử dụng' kết hợp với MemoryStream. Đây có phải là kịch bản mà câu lệnh 'sử dụng' không cần thiết không? Hoặc là nó hợp lệ để gọi trở lại bên trong của một tuyên bố 'sử dụng'?Liệu MemoryStream có được xử lý tự động khi trả lại nó như một ActionResult không?

EDIT:

Đối với mục đích của tôi, tôi đã phát hiện ra rằng sự ra đời của một 'sử dụng' tuyên bố không làm việc (ném một ObjectDisposedException). Dưới đây là những gì tôi đang thực hiện với phía khách hàng:

$('#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

Nó luôn luôn là một ý tưởng tốt để sử dụng 'sử dụng' tuyên bố khi giao dịch với các lớp mà thực hiện IDisposable, có hoặc không có tin ASP.NET đang xảy ra để làm sạch sau khi bạn. –

+1

'Tệp (luồng, chuỗi)' làm gì với luồng? thông thường đối tượng đã tạo luồng cũng nên xử lý luồng. trong trường hợp đó bạn sẽ chịu trách nhiệm xử lý luồng. –

+0

@GeorgeStocker có thể gọi phương thức 'Dispose()' không? Tôi nghĩ sau khi 'return' được gọi là nó sẽ không hoàn thành khối' using'. –

Trả lời

19

Có một MemoryStream được xử lý tự động khi trở về nó như một ActionResult?

Có, MVC (ít nhất là phiên bản 3) sẽ xóa sạch cho bạn. Bạn có thể mất một look at the source của phương pháp WriteFile trong 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); 
     } 
    } 
} 

Dòng using (FileStream) { sẽ đặt Stream trong một khối sử dụng, do đó Vứt bỏ nó khi nó đã viết nội dung để phản hồi HTTP.

Bạn cũng có thể xác minh hành vi này bằng cách tạo ra một dòng suối giả mà thực hiện điều này:

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

Vì vậy MVC sẽ vứt bỏ nó.

+0

Cảm ơn bạn đã trả lời dứt khoát. :) –

+0

... đủ hấp dẫn, nếu tôi sử dụng câu lệnh 'sử dụng' cho các ứng dụng của mình - tôi tạo ra lỗi máy chủ nội bộ. –

+0

@SeanAnderson Bạn có thể lấy dấu vết ngăn xếp và ngoại lệ không? Tùy thuộc vào Stream; nó có thể không muốn nhận được xử lý hai lần. – vcsjones

-3

Sau đây là mã hợp lệ phân phối luồng. Nếu được đính kèm theo số using, phương thức MemoryStream.Dispose() sẽ tự động được gọi khi trả lại.

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

Bạn có thể đạt được kết quả tương tự bằng cách đặt các đối tượng bên trong một khối try và sau đó gọi Dispose trong khối finally. Trong thực tế, theo tài liệu MSDN, đây là cách mà trình biên dịch sử dụng được dịch bởi trình biên dịch. Và trong một khối try..finally, finally sẽ luôn được thực thi ngay cả khi try thoát qua return.

Trình biên dịch sẽ dịch khối using như sau:

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

Liệu điều đó sẽ không làm dòng chảy từ bên dưới đối tượng Tệp? –

+1

@ LasseV.Karlsen Đó cũng là điều mà tôi đang tự hỏi. Và nếu nó không ... tại sao nó không? :) –

+0

+1 cho nhận xét @SeanAnderson Tôi muốn tự mình xem tài liệu này. – Craig

2

Sean: KHÔNG sử dụng 'đang sử dụng' vì nó sẽ Vứt bỏ đối tượng. Rời MVC truy cập một đối tượng Disposed. Do đó ngoại lệ (lỗi máy chủ) mà bạn gặp phải chắc chắn là một ObjectDisposedException. Hàm WriteFile được đăng trước đó Loại bỏ đối tượng cho bạn.

2

Một luồng bộ nhớ không cần thiết trong tình huống này. Bạn có thể tránh nó bằng cách tạo ra một ActionResult Tuỳ chỉnh như thế này:

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

Trên thực tế giải pháp khá tốt đẹp! Có vẻ như nó cũng sẽ lưu 1 bản sao đệm. (Có thể phụ thuộc vào một thỏa thuận lớn để giữ cho 'Chart' sống lâu như vậy) –