首页 > 解决方案 > C# CsvHelper:WriteRecord 抛出“ConfigurationException:不能自动映射继承 IEnumerable 的类型”

问题描述

这是我的原始代码,在没有 CsvHelper 的情况下写入 CSV 文件。

 public static void Write(string file, List<PayRecord> payrecords) {
   using(StreamWriter sw = new StreamWriter(file)) {
     sw.WriteLine("EmployeeId,Gross,Tax,Net");
     foreach(PayRecord c in payrecords) {
       string line = string.Format("{0},{1},{2},{3}",
         c.Id,
         c.Gross,
         c.Tax,
         c.Net);
       sw.WriteLine(line);
     }
   }
 }

这是相同的代码,但这次使用的是 CsvHelper:

public static void Write(string file, List<PayRecord> payrecords)
{
    using (StreamWriter sw = new StreamWriter(file))
    using (CsvWriter cw = new CsvWriter(sw, CultureInfo.CurrentCulture))
    {
        cw.WriteHeader<PayRecord>();
        foreach (var c in payrecords)
        {
            string line = string.Format("{0},{1},{2},{3}",
                c.Id,
                c.Gross,
                c.Tax,
                c.Net);
            cw.WriteRecord(line);

        }
    }
}

但是我遇到了以下异常:

Unhandled exception. CsvHelper.Configuration.ConfigurationException: Types that inherit IEnumerable cannot be auto mapped. Did you accidentally call GetRecord or WriteRecord which acts on a single record instead of calling GetRecords or WriteRecords which acts on a list of records?
   at CsvHelper.CsvWriter.WriteRecord[T](T record)

和:

Unhandled exception. System.ArgumentNullException: Value cannot be null. (Parameter 'source')
   at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
   at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source)
   at CsvHelper.Configuration.ClassMap.AutoMapConstructorParameters(ClassMap map, CsvContext context, LinkedList`1 mapParents, Int32 indexStart)
   at CsvHelper.Configuration.ClassMap.AutoMapConstructorParameters(ClassMap map, CsvContext context, LinkedList`1 mapParents, Int32 indexStart)
   at CsvHelper.Configuration.ClassMap.AutoMap(CsvContext context)
   at CsvHelper.CsvContext.AutoMap(Type type)
   at CsvHelper.CsvWriter.WriteHeader(Type type)
   at CsvHelper.CsvWriter.WriteHeader[T]()

怎么了?

标签: c#csvcsvhelper

解决方案


您正在尝试将 a 写string为记录。

cw.WriteRecord(line);

我无法重现您的ArgumentNullException内容,但我知道即使修复了该问题,您的代码仍然无法正常工作。

上面将抛出 a CsvHelper.Configuration.ConfigurationException,因为 astring被归类为单个字段,CsvHelper.WriteRecord并且更多的是用于类而不是 a string

其次,CsvWriter.WriteHeader不会将“光标”移动到下一行,因此您需要使用,CsvWriter.NextRecord()否则您的数据将全部与标题位于同一行。

以下代码有效:

  public static void Write(string file, List<PayRecord> payrecords) {
    using(StreamWriter sw = new StreamWriter(file))
    using(CsvWriter cw = new CsvWriter(sw, CultureInfo.CurrentCulture)) {
      cw.WriteHeader<PayRecord>();
      cw.NextRecord();

      foreach(var c in payrecords) {
        cw.WriteRecord(line);
      }
    }
}

但是,最好不要手动编写标题,使用CsvHelper.WriteRecords和传递整个对象集合,如下所示。

这导致正确使用更小、更清洁和更高效的代码CsvHelper.WriteRecords,同时也编写正确的标头。

更好

public static void Write(string file, List<PayRecord> payrecords)
{
    using (StreamWriter sw = new StreamWriter(file))
    using (CsvWriter cw = new CsvWriter(sw, CultureInfo.CurrentCulture))
    {
        cw.WriteRecords(payrecords);
    }
}

甚至更好?

要在不阻塞主线程的情况下执行 I/O 操作(写入 CSV 文件),请使用CsvHelper.WriteRecordsAsync, useawait using来不阻塞资源清理并使您的方法async能够await调用。

我也会使用ICollection<PayRecord>这样,如果您更改List<PayRecord>为任何其他实现的类型ICollection,则无需更改方法。

奶油

public static async Task WritePayRecords(string file, ICollection<PayRecord> payrecords)
{
    await using (StreamWriter sw = new StreamWriter(file))
    await using (CsvWriter cw = new CsvWriter(sw, CultureInfo.CurrentCulture))
    {
        if (payrecords == null)
            throw new ArgumentNullException(nameof(payrecords), "Collection of pay records cannot be null");

        await cw.WriteRecordsAsync(payrecords);
    }
}

上面的代码甚至可以使用null字段、null对象,并且如果集合是,也会抛出异常null(否则,CsvHelper 将抛出异常)。

希望有帮助!


推荐阅读