最近遇到了同样的问题,为了完整起见,发布另一个答案。我们有相当复杂的ClassMap<Worker> 类,并且确实想放松它。此外,我们需要 CSV 写入和读取,因此也需要来自 rob 答案的ExpandoObject。最终,这种方法就像 Michael Richardson 和 rob 答案的结合,应该会带来两全其美的效果。
最重要的是,为了在读取过程中区分 CSV 文件中的字典字段,最好在它们前面加上 "customerField." 之类的前缀。
首先我们需要字典与 Worker.customerField 之间的转换:
public static class WorkerExtensions
{
const string CustomerFieldPrefix = nameof(Worker.customerField) + ".";
public static dynamic GetCustomerFieldExpando(this Worker worker)
{
var expando = new ExpandoObject() as IDictionary<string, object>;
foreach (var fieldPair in worker.customerField)
{
expando[CustomerFieldPrefix + fieldPair.Key] = fieldPair.Value ?? "";
}
return expando;
}
public static void SetCustomerField(this Worker worker, ExpandoObject expando)
{
var columnsToValues = expando as IDictionary<string, object>;
foreach (var columnValuePair in columnsToValues)
{
if (columnValuePair.Key.StartsWith(CustomerFieldPrefix)
&& columnValuePair.Key.Length > CustomerFieldPrefix.Length)
{
string key = columnValuePair.Key.Substring(CustomerFieldPrefix.Length);
worker.customerField[key] = columnValuePair.Value;
}
}
}
}
接下来,CSV 写入可以同时使用ClassMap 和ExpandoObject,如下所示:
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.RegisterClassMap<WorkerMap>();
csv.WriteHeader<Worker>();
(workers.First().GetCustomerFieldExpando() as IDictionary<string, object>)
.Keys.ToList().ForEach(key => csv.WriteField(key));
csv.NextRecord();
foreach (var worker in workers)
{
csv.WriteRecord(worker);
csv.WriteRecord(worker.GetCustomerFieldExpando());
csv.NextRecord();
}
最后,CSV读取也可以结合ClassMap和ExpandoObject:
List<Worker> workers = new List<Worker>();
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.RegisterClassMap<WorkerMap>();
csv.Read();
csv.ReadHeader();
var columns = csv.Context.HeaderRecord;
while (csv.Read())
{
var worker = csv.GetRecord<Worker>();
workers.Add(worker);
ExpandoObject expando = csv.GetRecord<dynamic>();
worker.SetCustomerField(expando);
}
在 CSV 读取的情况下,如果您想将真实类型读入字典值(不仅仅是字符串),事情会变得更加复杂。我们需要字典键和数据类型之间的预定义关联,以便能够从ExpandoObject 转换为正确的类型。