JoshClose / CsvHelper

Library to help reading and writing CSV files
http://joshclose.github.io/CsvHelper/
Other
4.63k stars 1.05k forks source link

ConfigurationException: 'Types that inherit IEnumerable cannot be auto mapped' when using custom class based on DynamicObject (NOT ExpandoObject) which also implements IDictionary #2231

Closed eduarddejong closed 4 months ago

eduarddejong commented 4 months ago

Describe the bug Others have reported issues with this exception in the past, but as far as I can see it where all different situations, and what I am doing here is a bit more specific, and that is that I am using my own implementation based on the abstract class DynamicObject, which works pretty much the same as ExpandoObject, but you make your own implementation instead.

My implementation is as follows:

internal sealed class MyDynamicObject : DynamicObject, IDictionary<string, object?>, INotifyPropertyChanged
{
    // (implementation details)
}

The problem: IDictionary<TKey,TValue> is based on IEnumerable<KeyValuePair<TKey,TValue>> and the CsvWriter does not like this, and throws the exception mentioned in the subject. I am just trying to export CSV file from a list of objects of my class, using CsvWriter.WriteRecordsAsync.

When I remove IDictionary<string, object?> from my class, it suddenly works, without modifying internal details of my class. CsvWriter.WriteRecordsAsync does not throw an exception anymore.

However, my application depends on this implementation, as it relies on Dictionary behaviour as well with keys and values from my class (I internally use a dictionary). So I am breaking my code if I remove it.

Why can't I just use ExpandoObject instead? My class also has a couple of non-dynamic (real) properties and some custom behaviour. ExpandoObject is also sealed so we cannot derive from that one.

To Reproduce See above.

Expected behavior DynamicObject should be supported, and even if it implements IEnumerable (either directly or indirectly such as via IDictionary).

eduarddejong commented 4 months ago

I already found the solution! :

Turns out it actually works without any problem. I needed to cast my object to dynamic in order to make it work with CsvWriter.

And this does not only apply to DynamicObject, but also to ExpandoObject.

I wasn't aware of this.

JoshClose commented 4 months ago

Were you using it as a dynamic object? Meaning

dynamic o = new ExpandoObject();
eduarddejong commented 4 months ago

Oh, whoops. I see now that it's only picking up the dynamic properties of my DynamicObject implementation, and not the other properties. But it's not such a problem. I can still create an ExpandoObject and copy all the properties, I have already written some code in that direction inside a LINQ Select construct. I realize I probably already want to do that because I also have custom column names which I need as CSV header as well.

Were you using it as a dynamic object? Meaning

dynamic o = new ExpandoObject();

Thanks for your reply! I am actually doing as dynamic now in my Select and that also works, which works effectively the same (I do not add the dynamic properties using dot notation, I fill it dynamically like dictionary. That's why I also didn't need to use the dynamic keyword initially).