Search code examples
c#asp.net-mvccsvhelper

ASP.Net Export CSVHelper File - Cleanup


New to csvhelper, trying to make sure that I clean up\close all connections once user downloads file, not sure if I've done this right, could anyone advise what I need to do?

public ActionResult ExportClients()
{
    var token = new Token(this.User.Identity.Name);
    var clients =_clientService.Get(toekn.Id);

    var memoryStream = new MemoryStream();
    var streamWriter = new StreamWriter(memoryStream);
    var csvWriter = new CsvWriter(streamWriter);

    csvWriter.WriteRecords(clients);
    streamWriter.Flush();
    memoryStream.Position = 0;

    return File(memoryStream, "text/csv","clients.csv");                    
}

Solution

  • MemoryStream, StreamWriter and CsvWriter all implement IDisposable. So, let's take them one at a time:

    First, the MemoryStream

    FileStreamResult will dispose of the provided stream for you, which is the result of calling File at the end of the controller.

    Next up, the StreamWriter

    Ordinarily, you'd need to dispose of this yourself, with e.g. a using statement. In your example, disposing of CsvWriter will actually do this for you. However, there's a catch. By default:

    The StreamWriter object calls Dispose() on the provided Stream object when StreamWriter.Dispose is called.

    There's a constructor overload that allows for the underlying Stream to be left open. You'll need to use this; otherwise, the MemoryStream will get disposed before the FileStreamResult gets hold of it and you'll get the dreaded "object already disposed" exception.

    Finally, the CsvWriter

    This is the simplest case - You can just dispose of it yourself, using e.g. a using statement.


    Here's the revelant code:

    var memoryStream = new MemoryStream();
    var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8, 1024, true);
    
    using (var csvWriter = new CsvWriter(streamWriter))
    {
        csvWriter.WriteRecords(clients);
        // No need to flush as the StreamWriter's Dispose takes care of that.
    }
    
    memoryStream.Position = 0;
    
    return File(memoryStream, "text/csv","clients.csv");
    

    Unfortunately, there's no constructor for StreamWriter that allows you to provide just the Stream and the leaveOpen flag, so you have to use the one I've used above. The 1024 is the default value, but the Encoding.UTF8 is slightly different. It should suffice, but perhaps someone else will come along and point out how that can be better handled.