Search code examples
c#csvhelpercsvhelper.excel.core

Fit multiple Objects in one Row with CSVHelper in C#


i am trying to write two different Objects in one row with the C# library CSVHelper. It should look something like this:

obj1           obj2
-----------|------------
record1       record1
record2       record2

When register the class maps for these two objects and then call WriteRecords(List) and WriteRecords(List) these objects are written but they are not in the same row. Instead the records of obj2 are written in the rows following the records of obj1. It looks like this:

obj1
----------
record1
record2

obj2
----------
record1
record2

Program.cs:

string fileReadDirectory =
    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Stuecklisten");
string fileWriteDirectory =
    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Stueckliste.csv");
List<string> files = Directory.GetFiles(fileReadDirectory).ToList();
List<Part> parts = new List<Part>();
List<PartsPerList> partsPerLists = new List<PartsPerList>();
foreach (string file in files)
{
    //Reads records from Excel File
    CsvReader reader = new CsvReader(new ExcelParser(file));
    reader.Context.RegisterClassMap<ExcelSheetMap>();
    IEnumerable<Part>? excelRecords = reader.GetRecords<Part>();
    
    foreach (var record in excelRecords)
    {
        PartsPerList partsPerList = new PartsPerList();
        partsPerList.Listname = file;
        if (parts.Any(p => p.ManufacturerNr == record.ManufacturerNr))
        {
            Part part = parts.SingleOrDefault(p => p.ManufacturerNr == record.ManufacturerNr) ?? new Part();
            part.TotalQuantity += record.TotalQuantity;
        }
        else
        {
            parts.Add(record);
        }
        partsPerLists.Add(partsPerList);
    }
}
using (var stream = File.Open(fileWriteDirectory, FileMode.Create))
using (var streamWriter = new StreamWriter(stream))
using (var writer = new CsvWriter(streamWriter,CultureInfo.InvariantCulture))
{
    writer.Context.RegisterClassMap<ExcelSheetMap>();
    writer.Context.RegisterClassMap<ManufacturerPartsMap>();
    writer.WriteHeader(typeof(Part));
    writer.WriteRecords(parts);
    writer.WriteHeader(typeof(PartsPerList));
    writer.WriteRecords(partsPerLists);
}

Part.cs:

public class Part
{
    // public int Quantity { get; set; }
    public int TotalQuantity { get; set; }
    public string Description { get; set; } = string.Empty;
    public string Designator { get; set; } = string.Empty;
    public string Case { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
    public string Tolerance { get; set; } = string.Empty;
    public string Remark { get; set; } = string.Empty;
    public string PartNumber { get; set; } = string.Empty;
    public string Manufacturer { get; set; } = string.Empty;
    public string ManufacturerNr { get; set; } = string.Empty;
    public string RoHS { get; set; } = string.Empty;
    public string Nachweis { get; set; } = string.Empty;
    
}

Part Classmap:

public sealed class ExcelSheetMap : ClassMap<Part>
{
    public ExcelSheetMap()
    {
        // Map(m => m.Quantity).Name("Qty per pcs");
        Map(m => m.TotalQuantity).Index(0);
        Map(m => m.Description).Name("description");
        Map(m => m.Designator).Name("designator");
        Map(m => m.Case).Name("case");
        Map(m => m.Value).Name("value");
        Map(m => m.Tolerance).Name("tolerance");
        Map(m => m.Remark).Name("remark");
        Map(m => m.PartNumber).Name("partnumber");
        Map(m => m.Manufacturer).Name("manufacturer");
        Map(m => m.ManufacturerNr).Name("Manufactorer number");
        Map(m => m.RoHS).Name("RoHS");
        Map(m => m.Nachweis).Name("Nachweis");
    }
}

PartsPerList.cs:

public class PartsPerList
{
    public string Listname { get; set; } = string.Empty;
   
}

ManufacturersPartsMap.cs:

public class ManufacturerPartsMap : ClassMap<PartsPerList>
{
    public ManufacturerPartsMap()
    {
        Map(m => m.Listname).Name("test").Optional();
    }
}

Solution

  • To write two different objects in one row with CSVHelper, you can loop through the records and write them line by line.

    void Main()
    {
        var fooRecords = new List<Foo>
        {
            new Foo { Id = 1, Name = "one" },
            new Foo { Id = 2, Name = "two" },
        };
    
        var barRecords = new List<Bar>
        {
            new Bar { Id = 3, Description = "The first one" },
            new Bar { Id = 4, Description = "The secord one" },
        };
    
        //using (var writer = new StreamWriter("path\\to\\file.csv"))
        using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
        {
            csv.WriteHeader<Foo>();
            csv.WriteHeader<Bar>();
            csv.NextRecord();
            
            for (int i = 0; i < fooRecords.Count; i++)
            {
                csv.WriteRecord(fooRecords[i]);
                csv.WriteRecord(barRecords[i]);
                csv.NextRecord();
            }
        }
    }
    
    public class Foo
    {
        [Name("FooId")]
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class Bar
    {
        [Name("BarId")]
        public int Id { get; set; }
        public string Description { get; set; }
    }