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

Sono abituato a utilizzare l'istruzione 'using' in combinazione con MemoryStreams. È uno scenario in cui l'istruzione "using" non è necessaria? O è valido chiamare il ritorno all'interno di una dichiarazione 'using'?Un MemoryStream viene eliminato automaticamente quando lo si restituisce come ActionResult?

EDIT:

Per i miei scopi ho trovato che l'introduzione di un 'utilizzando' istruzione non funziona (tiri un ObjectDisposedException). Ecco cosa sto facendo con esso lato client:

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

E 'sempre una buona idea di utilizzare il 'utilizzando' istruzione quando si tratta di classi che implementano IDisposable, anche se non si crede ASP.NET è andare a pulire dopo di te. –

+1

cosa fa 'File (stream, string)' fare con lo stream? in genere l'oggetto che ha creato lo stream deve anche disporre del flusso. in tal caso sarai responsabile dello smaltimento del flusso. –

+0

@GeorgeStocker si potrebbe chiamare anche il metodo 'Dispose()'? Penserei dopo che 'return' è chiamato non completare il blocco' using'. –

risposta

19

fa un MemoryStream ottenere smaltiti automaticamente quando si ritorna come un ActionResult?

Sì, MVC (almeno versione 3) lo pulirà per voi. Si può prendere un look at the source del metodo WriteFile in 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); 
     } 
    } 
} 

La linea using (FileStream) { metterà il flusso in un blocco dell'uso, dello smaltimento in tal modo di esso quando ha scritto il contenuto della risposta HTTP.

Si può anche verificare questo comportamento creando un flusso fittizio che fa questo:

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

Così MVC sarà smaltire.

+0

Grazie per la risposta definitiva. :) –

+0

... abbastanza intrigante, se uso una istruzione 'using' per le mie applicazioni - creo un errore interno del server. –

+0

@SeanAnderson È possibile ottenere una traccia dello stack e un'eccezione? A seconda del flusso; potrebbe non piacergli essere eliminato due volte. – vcsjones

-3

Quanto segue è un codice valido che dispone del flusso. Se racchiuso in un blocco using, il metodo MemoryStream.Dispose() verrà richiamato automaticamente al momento della restituzione.

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

è possibile ottenere lo stesso risultato inserendo l'oggetto all'interno di un blocco try e quindi chiamando Dispose in un blocco finally. In effetti, secondo la documentazione MSDN, questo è il modo in cui l'istruzione using viene tradotta dal compilatore. E in un blocco try..finally lo finally verrà sempre eseguito anche quando try esce da return.

Il compilatore traduce il blocco using al seguente:

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

Non è possibile che lo stream venga estratto da sotto l'oggetto File? –

+1

@ LasseV.Karlsen Questo è quello che mi sto chiedendo anche io. E se non lo fa ... perché no? :) –

+0

+1 per il commento @SeanAnderson Mi piacerebbe vedere la documentazione su questo me stesso. – Craig

2

Sean: NON utilizzare "l'utilizzo" in quanto eliminerà l'oggetto. Lasciando MVC l'accesso a un oggetto Disposed. Quindi l'eccezione (errore del server) che hai riscontrato è certamente una ObjectDisposedException. La funzione WriteFile precedentemente pubblicata Disporre l'oggetto per te.

2

Un MemoryStream non è necessario in questa situazione. Si può evitare la creazione di un ActionResult personalizzato come questo:

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

Soluzione davvero carina! Sembra che salverebbe anche 1 copia buffer. (Può dipendere da quanto è grande mantenere il 'Chart' vivo così a lungo) –

Problemi correlati