Search code examples
c#.netcsvhelpersystem.io.file

How to write a CSV file after reading it with CsvHelper?


I need to write a HttpPostedFileBase csv (and save it) after mapping it into a list using the CsvHelper nuget package. However, after mapping it with CsvHelper, the ContentLength is 0, and I end up saving an empty .csv file.

CsvHelper itself states that the Read method should not be used when using the GetRecords<T>() method.

        // Summary:
        //     Gets all the records in the CSV file and converts each to System.Type T. The
        //     Read method should not be used when using this.
        //
        // Type parameters:
        //   T:
        //     The System.Type of the record.
        //
        // Returns:
        //     An System.Collections.Generic.IEnumerable`1 of records.
        public virtual IEnumerable<T> GetRecords<T>();

I tried placing it into a copy variable:

HttpPostedFileBase csvCopy = csvFile;

But this didn't work. Tried some other solutions I found on stackoverflow, which didn't work either. I "solved" this problem by sending the same file twice to the controller as a parameter. Then I use the first one with CsvHelper, and I read and save the other one.

 public async Task<ActionResult> ImportCSV(HttpPostedFileBase csvFile, HttpPostedFileBase csvFileCopy)

However, I think this is a bad solution. I would like to use a single file, map it, reread it and save it.

Mapping it into a list:

using(var reader = new StreamReader(csvFile.InputStream)) {
   using(var csvReader = new CsvReader(reader)) {
    csvReader.Configuration.RegisterClassMap(new CSVModelMap(mapDictionary));
    csvReader.Configuration.BadDataFound = null;
    csvReader.Configuration.HeaderValidated = null;
    csvReader.Configuration.MissingFieldFound = null;
    importData = csvReader.GetRecords<CSVModel>().ToList();    
   }
 }

Saving it:

var fileName = serverPath + "\\" + hashedFileName;
CheckIfDirectoryExists(serverPath);
var reader = new StreamReader(csvFile.InputStream);
var csvContent = await reader.ReadToEndAsync();
File.WriteAllText(fileName, csvContent);

Solution

  • The issue was CsvHelper:

    using(var csvReader = new CsvReader(reader))
    

    At the end of the using statement, CsvReader closes the reader. Then when I try

    csvFile.InputStream.Seek(0, SeekOrigin.Begin);
    

    or

    reader.BaseStream.Position = 0; 
    

    it throws a NullReferenceException. I solved this by simply overriding the CsvReader:

    using (var csvReader = new CsvReader(reader, true))
    

    true being leaveOpen:

            // Summary:
            //     Creates a new CSV reader using the given System.IO.TextReader.
            //
            // Parameters:
            //   reader:
            //     The reader.
            //
            //   leaveOpen:
            //     true to leave the reader open after the CsvReader object is disposed, otherwise
            //     false.
            public CsvReader(TextReader reader, bool leaveOpen);
    

    Then I set the position back to 0, using reader.BaseStream.Position = 0;, and then after saving the file, dispose the reader.