CsvHelper icon indicating copy to clipboard operation
CsvHelper copied to clipboard

InvalidOperationException while reading to a positional record with custom convert

Open mishamyte opened this issue 3 years ago • 1 comments

Describe the bug

When I try to read document, that has a Convert in it's class map to a positional record, CsvHelper fails with a exception

To Reproduce

I have a csv document, that contains unprocessed data in a cell (a list, separated by unique separator).

Id,Entries
Some,Entry1|Entry2

The corresponding entity could be defined

        public record Foo(
            string Id,
            IEnumerable<string> Entries);

With class map

        public sealed class FooMap  : ClassMap<Foo>
        {
            public FooMap()
            {
                AutoMap(CultureInfo.InvariantCulture); // Also I tried without AutoMap
                Map(x => x.Entries).Convert(args => args.Row
                    .GetField<string>(nameof(Foo.Entries))?
                    .Split('|')
                    .Select(entry=> entry.Trim())
                    .Where(entry => !string.IsNullOrEmpty(entry)) ?? Enumerable.Empty<string>());
            }
        }

Then I do the simple call:

                using var reader = new StreamReader(stream);
                using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);
                csv.Context.RegisterClassMap<FooMap>();
                return csv.GetRecords<Foo>();

Expected behavior No exception should appear, explicit Convert construction should be used

Additional context Here is the stacktrace from my business app

CsvHelper.ReaderException: An unexpected error occurred.
IReader state:
   ColumnCount: 0
   CurrentIndex: 1
   HeaderRecord:
["EmployeeId","Roles","CostCenterCode","OrgLevelId"]
IParser state:
   ByteCount: 0
   CharCount: 141
   Row: 2
   RawRow: 2
   Count: 4
   RawRecord:
SomeId,Role1|Role2|Role3|Role4,SomeCostCenterCode,SomeOrgLevelId


 ---> System.InvalidOperationException: Member is not a property or a field.
   at CsvHelper.TypeConversion.IEnumerableGenericConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)
   at lambda_method28(Closure )
   at CsvHelper.Expressions.RecordCreator.Create[T]()
   at CsvHelper.Expressions.RecordManager.Create[T]()
   at CsvHelper.CsvReader.GetRecords[T]()+MoveNext()
   --- End of inner exception stack trace ---
   at CsvHelper.CsvReader.GetRecords[T]()+MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at NameSpace.HandlerName(Request request, CancellationToken cancellationToken) in ...

mishamyte avatar Feb 07 '22 14:02 mishamyte

Also I need to add that everything will work if I will define my record as standard one

        public record Foo
        {
            public string Id { get; init; } = default!;

            public IEnumerable<string> Entries { get; init; } = new List<string>();
        }

mishamyte avatar Feb 07 '22 14:02 mishamyte