Reputation: 63
I was hoping to use a dynamically typed object to write to a CSV file.
I'm receiving a 'CsvHelper.CsvWriterException' within the CsvWriter.WriteObject method with this message: "No properties are mapped for type 'WpmExport.DynamicEntry'."
Here is the class that I'm trying use :
public class DynamicEntry : DynamicObject
{
private Dictionary<string, object> dictionary = new Dictionary<string, object>();
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
string name = binder.Name.ToLower();
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name.ToLower()] = value;
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return dictionary.Keys.AsEnumerable();
}
}
Anyone with any ideas or working examples? The documentation at http://joshclose.github.io/CsvHelper/ hints that it is possible but doesn't provide any guidance.
TIA
Upvotes: 3
Views: 3868
Reputation: 113
Because I cannot wait for Version 3.0 (and CsvHelper.Excel to support it), I have found a interim-solution.
Got the class to export:
public partial class EntryReportInventory
{
public Guid DeviceId { get; set; }
[ReportProperty]
public string DeviceName { get; set; }
public Dictionary<string, object> InventoryValues { get; set; }
public EntryReportInventory(Device device, Dictionary<string, object> inventoryValues)
{
this.DeviceId = device.Id;
this.DeviceName = device.Name;
this.InventoryValues = inventoryValues;
}
}
Created Mapper:
Type genericClass = typeof(DefaultCsvClassMap<>);
Type constructedClass = genericClass.MakeGenericType(typeof(EntryReportInventory));
return (CsvClassMap)Activator.CreateInstance(constructedClass);
And now the magic. I iterate all properties.
foreach (PropertyInfo property in mapping)
{
...
if (isInventoryReportBaseType && typeof(Dictionary<string, object>).IsAssignableFrom(property.PropertyType))
{
var dataSource = (ReportInventoryBase)Activator.CreateInstance(entityType, dbContext);
foreach (var item in dataSource.ColumnNameAndText)
{
var columnName = item.Key;
var newMap = new CsvPropertyMap(property);
newMap.Name(columnName);
newMap.TypeConverter(new InventoryEntryListSpecifiedTypeConverter(item.Key));
customMap.PropertyMaps.Add(newMap);
}
...
}
And my converter is:
public class InventoryEntryListSpecifiedTypeConverter : CsvHelper.TypeConversion.ITypeConverter
{
private string indexKey;
public InventoryEntryListSpecifiedTypeConverter(string indexKey)
{
this.indexKey = indexKey;
}
public bool CanConvertFrom(Type type)
{
return true;
}
public bool CanConvertTo(Type type)
{
return true;
}
public object ConvertFromString(TypeConverterOptions options, string text)
{
throw new NotImplementedException();
}
public string ConvertToString(TypeConverterOptions options, object value)
{
var myValue = value as Dictionary<string, object>;
if (value == null || myValue.Count == 0) return null;
return myValue[indexKey] + "";
}
}
Don't know why, but it works to pass the same property several times. That's it :) You only have to have a list before (here: dataSource.ColumnNameAndText, filled from an external source) to identify the columns/values.
Upvotes: 2
Reputation: 23383
The functionality does not exist yet. You can write dynamic
but not DynamicObject
. You can view a thread on the subject here. https://github.com/JoshClose/CsvHelper/issues/187
When the functionality get implemented, I'll update the answer with the version it's in.
Update
This functionality will be available in 3.0. You can currently try out the 3.0-beta from NuGet.
Upvotes: 4