Search code examples
c#csvhelperline-numbers

CsvHelper Write Row Number


I am using CsvHelper to export data into a file and I need to include the line numbers.

Since my data comes from SQL Server, I just added a LineNumber into the Class and populate it using ROW_NUMBER() via TSQL and works just fine. I am thinking maybe CsvHelper already has a way of doing this.

I have tried the following, but doesn't work:

public sealed class DetailMap : ClassMap<Detail>
{
    public DetailMap()
    {
        Map().Convert(r => r.Row.Context.Writer.Row).Index(0);
        Map(d => d.Field1).Index(1);
        Map(d => d.Field2).Index(2);
    }
}

It just skips the first index.

Just sharing, in CsvReader, I was able to set the line number like this (this works in reading):

public sealed class DetailMap : ClassMap<Detail>
{
    public DetailMap()
    {
        Map(d => d.LineNumber).Convert(r => r.Row.Context.Parser.Row);
        Map(d => d.Field1).Index(0);
        Map(d => d.Field2).Index(1);
    }
}

Does CsvHelper have some kind of mechanism to be able to write the Line Number without having to populate it outside the classmap?


Solution

  • According to this comment, the overload of Convert you are using is only for reading (Convert was previously called ConvertUsing). There is an overload for writing but it does not have access to the IReaderRow, instead it uses the current instance of your class for that row.

    Instead you can define a custom ITypeConverter:

    public class RowNumberConverter : DefaultTypeConverter
    {
        public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
        {
            var rowNumber = row.Context.Writer.Row;
            if (row.Configuration.HasHeaderRecord)
                rowNumber--;
    
            return rowNumber.ToString();
        }
    }
    

    And then apply this to your line number field:

    Map(d => d.LineNo).TypeConverter<RowNumberConverter>().Index(0);

    The above example assumes that you want your first row of data to be line number 1, even if you have a header row. If this is not the case you can remove the extra code and just return row.Context.Writer.Row.ToString()