我们可以使用 CsvHelper 为我们的属性同时使用 Index 和 Name 属性吗?

Can we use both Index and Name attributes for our properties with CsvHelper?

我是使用 CsvHelper API 编写 CSV 文件的新手。我已阅读 Getting Started 帮助主题。

我需要创建一个只定义 headers 的空 CSV 文件。这样用户就可以从此模板文件开始并填充数据。

我知道如何手动创建文本文件并自己手动创建。但是由于我的 class 具有读取 CSV 的属性,我想知道我是否可以利用它来做相反的事情?

我在那里看到了一个 WriteHeader 的例子,所以我想出了:

public void CreateEmptyHistoryFile(string path)
{
    using (var writer = new StreamWriter(path))
    using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
    {
        csv.WriteHeader<GenericHistory>();
    }
}    

但是如果可以将两个这些属性添加到我的属性中,我找不到它的文档:

[Index(1)]
[Name(“xxx”)]
Public string Yyy …

链接的文章说:

Remember how we can't rely on property order in .NET? If we are writing a class that has a header, it doesn't matter, as long as we are reading using the headers later. If we want to position the headers in the CSV file, we need to specify an index to guarantee it's order. It's recommended to always set an index when writing.

有道理吗?我们可以混合使用这两个属性吗?


更新

我想知道关于属性顺序的建议是否过时了?

我只是 运行 我的代码,输出是按写入顺序(我没有添加 Index 属性)。


更新

我的 class 有 82 个属性,我试图为每个 属性 添加一个 Index 属性。当我尝试阅读时出现异常:

  <LogEntry Date="2022-01-22 18:38:03" Severity="Exception" Source="MSAToolsLibrary.Importer.Importer.ImportHistoryFromGeneric" ThreadId="1">
    <Exception Type="CsvHelper.TypeConversion.TypeConverterException" Source="CsvHelper.TypeConversion.DefaultTypeConverter.ConvertFromString">
      <Message>The conversion cannot be performed.
    Text: 'Andrew Truckle'
    MemberType: System.Int32
    TypeConverter: 'CsvHelper.TypeConversion.Int32Converter'
IReader state:
   ColumnCount: 0
   CurrentIndex: 4
   HeaderRecord:
["Week","MidweekMeeting","WeekendMeeting","NumClasses","Host","Cohost","Chairman","AuxCounsellor1","AuxCounsellor2","PrayerOpen","Treasures_Name","Treasures_Theme","Treasures_Method","Gems_Name","Teaching","Teaching_Class1_Name","Teaching_Class2_Name","Teaching_Class3_Name","BibleReading_Study","StudentItem1_Study","StudentItem2_Study","StudentItem3_Study","StudentItem4_Study","StudentItem1_Description","StudentItem2_Description","StudentItem3_Description","StudentItem4_Description","BibleReading_Class1_Name","BibleReading_Class2_Name","BibleReading_Class3_Name","StudentItem1_Class1_Name","StudentItem1_Class1_Assistant","StudentItem2_Class1_Name","StudentItem2_Class1_Assistant","StudentItem3_Class1_Name","StudentItem3_Class1_Assistant","StudentItem4_Class1_Name","StudentItem4_Class1_Assistant","StudentItem1_Class2_Name","StudentItem1_Class2_Assistant","StudentItem2_Class2_Name","StudentItem2_Class2_Assistant","StudentItem3_Class2_Name","StudentItem3_Class2_Assistant","StudentItem4_Class2_Name","StudentItem4_Class2_Assistant","StudentItem1_Class3_Name","StudentItem1_Class3_Assistant","StudentItem2_Class3_Name","StudentItem2_Class3_Assistant","StudentItem3_Class3_Name","StudentItem3_Class3_Assistant","StudentItem4_Class3_Name","StudentItem4_Class3_Assistant","Living1_Name","Living1_Theme","Living1_Method","Living2_Name","Living2_Theme","Living2_Method","Living3_Name","Living3_Theme","Living3_Method","CBS_Conductor","CBS_Reader","PrayerClose","Weekend_Host","Weekend_Cohost","Weekend_Chairman","Weekend_PrayerOpen","Weekend_PT_Speaker","Weekend_PT_Theme","Weekend_PT_Number","Weekend_PT_Hospitality","Weekend_PT_Interpreter","Weekend_AT1_Name","Weekend_AT2_Name","Weekend_WT_Conductor","Weekend_WT_Reader","Weekend_WT_BibleVersesReader","Weekend_PrayerClose","Weekend_Misc"]
IParser state:
   ByteCount: 0
   CharCount: 2289
   Row: 2
   RawRow: 2
   Count: 82
   RawRecord:
27/01/2020,Y,Y,1,...

</Message>
      <StackTrace>   at CsvHelper.TypeConversion.DefaultTypeConverter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)
   at CsvHelper.TypeConversion.Int32Converter.ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData)
   at lambda_method(Closure )
   at CsvHelper.Expressions.RecordCreator.Create[T]()
   at CsvHelper.CsvReader.&lt;GetRecords&gt;d__87`1.MoveNext()
   at MSAToolsLibrary.Importer.Importer.ImportHistoryFromGeneric()</StackTrace>
    </Exception>
  </LogEntry>

出于隐私原因,我已删除数据中的名称。我注意到它指的是当前索引 4 并执行整数转换。如您所见,该值为 1。在读class:

[Index(4)]
public int NumClasses { get => HistoryWeek.NumClasses; set => HistoryWeek.NumClasses = value; }

正如我所说,如果我删除所有 [Index(..)],我的阅读代码仍然有效。但是如果我 re-introduce 所有索引值都会失败。

我通常发现它的顺序与您 class 中声明的属性的顺序相同。但是,如果您想确定它们是按特定顺序排列的,我会同时使用两者。

Type.GetProperties Method

The GetProperties method does not return properties in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which properties are returned, because that order varies.

您可以试试这个例子,看看 NameIndex 属性在使用 CsvWriter 时都有效。

void Main()
{
    using (var csv = new CsvWriter(Console.Out, CultureInfo.InvariantCulture))
    {
        csv.WriteHeader<Foo>();
    }
}

public class Foo
{
    [Name("Identity")]
    [Index(1)]
    public int Id { get; set; }
    [Name("FirstName")]
    [Index(0)]
    public string Name { get; set; }
}