Using Custom TypeConverter causes dangerous/spurious CsvWriter behavior
jzabroski opened this issue · comments
Describe the bug
On CsvHelper 31.0.4:
When using a custom TypeConverter:
- data can be completely omitted, causing columns to shift left one or more columns
- data can be written out-of-order from the column headers
To Reproduce
This is not a 100% complete repro yet, but illustrative of the problem.
SodPosition is a gRPC object that I cannot concisely copy-paste. I plan to work on whittling this down to a simpler repro.
I've mangled the names a bit to abstract away what problem domain this is for.
public class RejectedAmendment
{
public SodPosition SodPosition { get; set; }
public string ValidationMessage { get; set; }
public override string ToString()
{
return $"ValidationMessage: {ValidationMessage}\tSodPosition:{SodPosition}";
}
}
public sealed class RejectedAmendmentClassMap : ClassMap<RejectedAmendment>
{
public RejectedAmendmentClassMap()
{
Map(x => x.SodPosition.S).Name("S");
Map(x => x.SodPosition.F).Name("F");
Map(x => x.SodPosition.P).Name("P");
Map(x => x.SodPosition.PositionGroup).Name("PositionGroup");
Map(x => x.SodPosition.AType).Name("AType");
Map(x => x.SodPosition.OptionalQuantityCase, false).Name("Quantity").TypeConverter<OptionalQuantityOneofCaseTypeConverter>();
/*
// This works fine
.Convert(args =>
{
return (args.Value.SodPosition.GetQuantity() ?? 0).ToString(CultureInfo.InvariantCulture);
});*/
Map(x => x.SodPosition.C, false).Name("C");
Map(x => x.SodPosition.PR, false).Name("PR");
Map(x => x.SodPosition.FXR, false).Name("FXR");
Map(x => x.ValidationMessage).Name("ValidationMessage");
}
}
public class OptionalQuantityOneofCaseTypeConverter : ITypeConverter
{
public object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
throw new System.NotImplementedException();
}
public string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
if (value is SodPosition sodPosition)
{
return (sodPosition.GetQuantity() ?? 0).ToString(CultureInfo.InvariantCulture);
}
return null;
}
}
protected string WriteCsv<T, TClassMap>(ICollection<T> data, string folder, string filename)
where TClassMap : ClassMap<T>
{
if (data == null) throw new ArgumentNullException(nameof(data));
if (!data.Any())
{
throw new Exception($"data missing for {typeof(T)}");
}
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentNullException(nameof(filename));
var path = GetFullPath(folder, filename);
if (path == null) throw new ArgumentNullException(nameof(path));
if (_fileSystem.File.Exists(path))
{
throw new Exception($"File with name [{filename}] already exists at [{path}].");
}
using (var writer = new StreamWriter(path))
{
using (var csvWriter = new CsvWriter(writer, GetCsvConfiguration()))
{
csvWriter.Context.RegisterClassMap<TClassMap>();
csvWriter.WriteHeader<T>();
csvWriter.NextRecord();
foreach (var record in data)
{
csvWriter.WriteRecord(record);
csvWriter.NextRecord();
}
}
}
_log.Debug($"Saved file to path [{path}]");
return path;
}
Expected behavior
Data to be written to the correct columns.
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
I experimented with the useExistingMap overload of Map(x => x...) to see if that had any bearing. It had a slight bearing in that if I also re-arranged a few columns, I could get the problem to improve (columns stopped appearing out of order), but the FXR column would still not write to the file.