I have an immutable class that I want to write to and read from a CSV file. The issue is I am getting an exception when reading the CSV despite having mapped the object and set up a configuration that should allow this to work.
To do this I am using CsvHelper. The immutable class looks like the following.
public class ImmutableTest
{
public Guid Id { get; }
public string Name { get; }
public ImmutableTest(string name) : this(Guid.NewGuid(), name)
{
}
public ImmutableTest(Guid id, string name)
{
Id = id;
Name = name;
}
}
I have no issue writing this to a CSV file, but when I try to read it from a file, I get the following exception.
No members are mapped for type 'CsvTest.Program+ImmutableTest'
However, I have mapped the members for this class in the map class below.
public sealed class ImmutableTestMap : ClassMap<ImmutableTest>
{
public ImmutableTestMap()
{
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
Map(immutableTest => immutableTest.Name)
.Index(1)
.Name(nameof(ImmutableTest.Name));
}
}
I have also tried to configure the reader to use the constructor to build the object by using the following configuration.
Configuration config = new Configuration
{
IgnoreBlankLines = true
};
config.RegisterClassMap<ImmutableTestMap>();
config.ShouldUseConstructorParameters = type => true;
config.GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault();
No of this seems to be working. Where am I going wrong?
Complete MCVE .NET Framework Console Example
Install the packages
Install-Package CsvHelper
Install-Package morelinq
Sample console program
using System;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;
using MoreLinq;
namespace CsvTest
{
class Program
{
static void Main()
{
Configuration config = new Configuration
{
IgnoreBlankLines = true
};
config.RegisterClassMap<ImmutableTestMap>();
config.ShouldUseConstructorParameters = type => true;
config.GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault();
const string filePath = "Test.csv";
using (FileStream file = new FileStream(filePath, FileMode.Create))
using (StreamWriter fileWriter = new StreamWriter(file))
using (CsvSerializer csvSerializer = new CsvSerializer(fileWriter, config))
using (CsvWriter csvWriter = new CsvWriter(csvSerializer))
{
csvWriter.WriteHeader<ImmutableTest>();
csvWriter.NextRecord();
csvWriter.WriteRecord(new ImmutableTest("Test 1"));
csvWriter.NextRecord();
csvWriter.WriteRecord(new ImmutableTest("Test 2"));
csvWriter.NextRecord();
}
using (FileStream file = new FileStream(filePath, FileMode.Open))
using (StreamReader fileReader = new StreamReader(file))
using (CsvReader csvReader = new CsvReader(fileReader, config))
{
foreach (ImmutableTest record in csvReader.GetRecords<ImmutableTest>())
{
Console.WriteLine(record.Id);
Console.WriteLine(record.Name);
Console.WriteLine();
}
}
}
public sealed class ImmutableTestMap : ClassMap<ImmutableTest>
{
public ImmutableTestMap()
{
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
Map(immutableTest => immutableTest.Name)
.Index(1)
.Name(nameof(ImmutableTest.Name));
}
}
public class ImmutableTest
{
public Guid Id { get; }
public string Name { get; }
public ImmutableTest(string name) : this(Guid.NewGuid(), name)
{
}
public ImmutableTest(Guid id, string name)
{
Id = id;
Name = name;
}
}
}
}
If the type is immutable, it will use constructor mapping instead. Your constructor variable names need to match the header names. You can do this using Configuration.PrepareHeaderForMatch
.
void Main()
{
var s = new StringBuilder();
s.AppendLine("Id,Name");
s.AppendLine($"{Guid.NewGuid()},one");
using (var reader = new StringReader(s.ToString()))
using (var csv = new CsvReader(reader))
{
csv.Configuration.PrepareHeaderForMatch = (header, indexer) => header.ToLower();
csv.GetRecords<ImmutableTest>().ToList().Dump();
}
}
public class ImmutableTest
{
public Guid Id { get; }
public string Name { get; }
public ImmutableTest(string name) : this(Guid.NewGuid(), name)
{
}
public ImmutableTest(Guid id, string name)
{
Id = id;
Name = name;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With