0

We are porting legacy code to dotnet standard and are facing an issue which [I believe] wasn't present in CsvHelper 2.5.0 net451.

The issue appears to be that CsvReader can't manage to resolve the correct ReturnType of the underlying properties in the mappings and is trying to map them all to System.Object

I have managed to reproduce it with a unit test in your solution:

[TestMethod]
public void CanCreateMapsFromDynamicList()
{
    var data = new List<string[]>
    {
        new[] { "Col1", "Col2" },
        new[] { "1", "one" },
        new[] { "2", "two" }
    };

    var queue = new Queue<string[]>(data);
    var parserMock = new ParserMock(queue);

    var csvReader = new CsvReader(parserMock);
    csvReader.Configuration.RegisterClassMap<SomeTypeClassMap>();

    var records = csvReader.GetRecords<SomeType>().ToList();

    Assert.IsNotNull(records);
    Assert.AreEqual(2, records.Count);

}

public class SomeTypeClassMap : ClassMap<SomeType>
{
    public SomeTypeClassMap()
    {
        Map(x => x.Id).Ignore();
        var t = new SomeType();

        foreach (var mapping in t.Mappings)
        {
            Map(mapping);
        }
    }
}

public class SomeType
{
    public int Id { get; set; }
    public string Col1 { get; set; }
    public string Col2 { get; set; }

    public IEnumerable<Expression<Func<SomeType, dynamic>>> Mappings =>
        new List<Expression<Func<SomeType, dynamic>>> {i => i.Col1, i => i.Col2};
}

Error reported:

Test method CsvHelper.Tests.CsvReaderMappingTestsRuslan.CanCreateMapsFromDynamicList threw exception: System.InvalidCastException: Unable to cast object of type 'CsvHelper.Configuration.MemberMap2[CsvHelper.Tests.CsvReaderMappingTestsRuslan+SomeType,System.String]' to type 'CsvHelper.Configuration.MemberMap2[CsvHelper.Tests.CsvReaderMappingTestsRuslan+SomeType,System.Object]'.

P.S. I've tried changing "Mappings" property to static and getting the same result.

Ruslan
  • 9,927
  • 15
  • 55
  • 89

1 Answers1

0

resolved it by adding this overload method:

    public virtual MemberMap Map<T>( Expression<Func<T, object>> expression, bool useExistingMap = true )
    {
        var stack = ReflectionHelper.GetMembers(expression);
        if (stack.Count == 0)
        {
            throw new InvalidOperationException("No members were found in expression '{expression}'.");
        }

        ClassMap currentClassMap = this;
        MemberInfo member;

        if (stack.Count > 1)
        {
            // We need to add a reference map for every sub member.
            while (stack.Count > 1)
            {
                member = stack.Pop();
                Type mapType;
                var property = member as PropertyInfo;
                var field = member as FieldInfo;
                if (property != null)
                {
                    mapType = typeof(DefaultClassMap<>).MakeGenericType(property.PropertyType);
                }
                else if (field != null)
                {
                    mapType = typeof(DefaultClassMap<>).MakeGenericType(field.FieldType);
                }
                else
                {
                    throw new InvalidOperationException("The given expression was not a property or a field.");
                }

                var referenceMap = currentClassMap.References(mapType, member);
                currentClassMap = referenceMap.Data.Mapping;
            }
        }

        // Add the member map to the last reference map.
        member = stack.Pop();

        return currentClassMap.Map( typeof(TClass), member, useExistingMap );
    }

PR raised https://github.com/JoshClose/CsvHelper/pull/1505

Ruslan
  • 9,927
  • 15
  • 55
  • 89