C# 加载到DataTable时NullValues选项不起作用

C# 加载到DataTable时NullValues选项不起作用,c#,.net,csvhelper,C#,.net,Csvhelper,在将CSV读入数据表时,我试图添加布尔值和空值的选项,这些选项似乎不起作用。例如,包含以下类似数据的文件: Id,MaxDiscount,Name,Active,AltId 1,,Foo,1,ABC123 2,10,Bar,0,DEF345 以及以下使用模式文件动态获取我们期望的头和数据类型的逻辑: var dt = new DataTable(); using (var reader = new StreamReader(file.FullName)) using (var csv = ne

在将CSV读入数据表时,我试图添加布尔值和空值的选项,这些选项似乎不起作用。例如,包含以下类似数据的文件:

Id,MaxDiscount,Name,Active,AltId
1,,Foo,1,ABC123
2,10,Bar,0,DEF345
以及以下使用模式文件动态获取我们期望的头和数据类型的逻辑:

var dt = new DataTable();
using (var reader = new StreamReader(file.FullName))
using (var csv = new CsvReader(reader))
{
    csv.Configuration.HasHeaderRecord = true;
    csv.Configuration.IgnoreQuotes = false;
    csv.Configuration.TypeConverterOptionsCache.GetOptions<int>().NullValues.Add(string.Empty);
    csv.Configuration.TypeConverterOptionsCache.GetOptions<bool>().BooleanFalseValues.Add("0");
    csv.Configuration.TypeConverterOptionsCache.GetOptions<bool>().BooleanTrueValues.Add("1");

    using (var dr = new CsvDataReader(csv))
    {
        foreach (var p in schema.Properties)
        {
            var type = Type.GetType(p.Type, true, true);
            var dc = new DataColumn
            {
                ColumnName = p.Name,
                Unique = p.IsId,
                AllowDBNull = p.Nullable,
                DataType = type
            };

            dt.Columns.Add(dc);
        }
        dt.Load(dr);
    }
}
在这种情况下,架构中的属性将与CSV文件中的列相关。理论上,这将允许我在运行时解析文件并验证数据类型,而不是每次引入新的CSV布局时都必须创建新的对象模型。

From

如果要指定列和列类型,数据表将加载自动转换的类型

我看到的是,在使用CsvDataReader时忽略CsvReader类型转换器选项

但是如果您使用csv.GetRecords,它将使用定义的类型转换器选项

List<csvData> result = csv.GetRecords<csvData>().ToList();

如果要指定列和列类型,数据表将加载自动转换的类型

我看到的是,在使用CsvDataReader时忽略CsvReader类型转换器选项

但是如果您使用csv.GetRecords,它将使用定义的类型转换器选项

List<csvData> result = csv.GetRecords<csvData>().ToList();
[第二次尝试]

只要数据列集合是由CsvDataReader和配置创建的,我就可以通过CsvDataReader将数据加载到DataTable对象中。分隔符设置为逗号,但。。。布尔字段活动不是真正的布尔值

根据我的测试和对文档的理解,只有一种方法可以获得正确的数据——通过helper类,它需要设置为fields。其中两项非常重要:

BooleanFalseValuesAttribute用于表示 转换时布尔值为false。 BooleanTrueValuesAttribute转换时用于表示布尔真值的字符串值

因此,班级的装饰可能看起来像:

public class MyData
{
    [Name("Id")]
    public int Id { get; set; }
    [Name("MaxDiscount")]
    public int? MaxDiscount { get; set; }
    [Name("Name")]
    public string Name { get; set; }
    [Name("Active")]
    [BooleanTrueValues("1")]
    [BooleanFalseValues("0")]
    public bool? Active { get; set; }
    [Name("AltId")]
    public string AltId { get; set; }
}
和帮助器类,用于映射字段:

public class MyDataMapper: ClassMap<MyData>
{
    public MyDataMapper()
    {
        Map(m => m.Id);
        Map(m => m.MaxDiscount);
        Map(m => m.Name);
        Map(m => m.Active);
        Map(m => m.AltId);
    }
}
见:

如果我很了解您,您希望将数据提取到DataTable对象中。。。看看这个:

List<MyData> records = null;
using (var reader = new StreamReader(myfile))
using (var csv = new CsvReader(reader))
{
    csv.Configuration.HasHeaderRecord = true;
    csv.Configuration.IgnoreQuotes = false;
    csv.Configuration.Delimiter = ",";
    csv.Configuration.RegisterClassMap<MyDataMapper>();
    records = csv.GetRecords<MyData>().ToList();
    dt = records.Select(x=>dt.LoadDataRow(new object[]
            {
                x.Id,
                x.MaxDiscount,
                x.Name,
                x.Active,
                x.AltId
            },false))
            .CopyToDataTable();
     dt.Dump();
[第二次尝试]

只要数据列集合是由CsvDataReader和配置创建的,我就可以通过CsvDataReader将数据加载到DataTable对象中。分隔符设置为逗号,但。。。布尔字段活动不是真正的布尔值

根据我的测试和对文档的理解,只有一种方法可以获得正确的数据——通过helper类,它需要设置为fields。其中两项非常重要:

BooleanFalseValuesAttribute用于表示 转换时布尔值为false。 BooleanTrueValuesAttribute转换时用于表示布尔真值的字符串值

因此,班级的装饰可能看起来像:

public class MyData
{
    [Name("Id")]
    public int Id { get; set; }
    [Name("MaxDiscount")]
    public int? MaxDiscount { get; set; }
    [Name("Name")]
    public string Name { get; set; }
    [Name("Active")]
    [BooleanTrueValues("1")]
    [BooleanFalseValues("0")]
    public bool? Active { get; set; }
    [Name("AltId")]
    public string AltId { get; set; }
}
和帮助器类,用于映射字段:

public class MyDataMapper: ClassMap<MyData>
{
    public MyDataMapper()
    {
        Map(m => m.Id);
        Map(m => m.MaxDiscount);
        Map(m => m.Name);
        Map(m => m.Active);
        Map(m => m.AltId);
    }
}
见:

如果我很了解您,您希望将数据提取到DataTable对象中。。。看看这个:

List<MyData> records = null;
using (var reader = new StreamReader(myfile))
using (var csv = new CsvReader(reader))
{
    csv.Configuration.HasHeaderRecord = true;
    csv.Configuration.IgnoreQuotes = false;
    csv.Configuration.Delimiter = ",";
    csv.Configuration.RegisterClassMap<MyDataMapper>();
    records = csv.GetRecords<MyData>().ToList();
    dt = records.Select(x=>dt.LoadDataRow(new object[]
            {
                x.Id,
                x.MaxDiscount,
                x.Name,
                x.Active,
                x.AltId
            },false))
            .CopyToDataTable();
     dt.Dump();
在我看来,CsvDataReader类是无用的——GetFieldType的实现返回TypeOfsString,GetValue也返回字符串,因此尽管它实现了类型化数据访问器方法,但DataTable类加载方法从未调用它们

因此,不会发生CsvHelper映射-转换由DataTable使用标准字符串到类型转换器完成

我建议取消CsvDataReader类的使用,并替换dt.Loaddr;用这样的方式打电话:

static void Load(DataTable dt, CsvReader csv)
{
    if (csv.Configuration.HasHeaderRecord)
    {
        if (!csv.Read()) return;
        csv.ReadHeader();
    }
    var valueTypes = new Type[dt.Columns.Count];
    for (int i = 0; i < valueTypes.Length; i++)
    {
        var dc = dt.Columns[i];
        var type = dc.DataType;
        if (dc.AllowDBNull && type.IsValueType)
            type = typeof(Nullable<>).MakeGenericType(type);
        valueTypes[i] = type;
    }
    var valueBuffer = new object[valueTypes.Length];
    dt.BeginLoadData();
    while (csv.Read())
    {
        for (int i = 0; i < valueBuffer.Length; i++)
            valueBuffer[i] = csv.GetField(valueTypes[i], i);
        dt.LoadDataRow(valueBuffer, true);
    }
    dt.EndLoadData();
}
基本上是准备列类型映射,并使用CsvReader.GetFieldtype索引方法填充数据行值。这样,转换由CsvReader类执行,并将使用所有转换选项

顺便说一句,所显示的布尔值或空值选项都不是真正需要的-它们都由CsvHelper默认类型转换器处理。

在我看来,CsvDataReader类是无用的-GetFieldType的实现返回typeofstring,GetValue也返回字符串,因此尽管它实现了类型化数据访问器方法,DataTable类装入方法从不调用它们

因此,不会发生CsvHelper映射-转换由DataTable使用标准字符串到类型转换器完成

我建议取消CsvDataReader类的使用,并替换dt.Loaddr;用这样的方式打电话:

static void Load(DataTable dt, CsvReader csv)
{
    if (csv.Configuration.HasHeaderRecord)
    {
        if (!csv.Read()) return;
        csv.ReadHeader();
    }
    var valueTypes = new Type[dt.Columns.Count];
    for (int i = 0; i < valueTypes.Length; i++)
    {
        var dc = dt.Columns[i];
        var type = dc.DataType;
        if (dc.AllowDBNull && type.IsValueType)
            type = typeof(Nullable<>).MakeGenericType(type);
        valueTypes[i] = type;
    }
    var valueBuffer = new object[valueTypes.Length];
    dt.BeginLoadData();
    while (csv.Read())
    {
        for (int i = 0; i < valueBuffer.Length; i++)
            valueBuffer[i] = csv.GetField(valueTypes[i], i);
        dt.LoadDataRow(valueBuffer, true);
    }
    dt.EndLoadData();
}
基本上是准备列类型映射,并使用CsvReader.GetFieldtype索引方法填充数据行值。这样,转换由CsvReader类执行,并将使用所有转换选项


顺便说一句,所显示的布尔值或空值选项都不是真正需要的-它们都由CsvHelper默认类型转换器处理。

架构定义在哪里/如何定义?如果使用可为空的int版本GetOptions而不是不可为空的版本GetOptions,会发生什么情况?@grek40架构是从包含所使用CSV信息的JSON文件中解析出来的阅读当我使用nullable int选项时,我得到了e

xact相同错误您可以发布您的模式信息吗?@WAELABAS模式信息已发布,谢谢!架构定义在何处/如何定义?如果使用可为null的int版本GetOptions而不是不可为null的版本,会发生什么情况?@grek40架构是从包含所读取CSV信息的JSON文件中解析出来的。当我使用nullable int选项时,我得到了完全相同的错误。你能发布你的模式信息吗?@WaelAbbas模式信息已经发布,谢谢!若要重现此问题,您必须手动添加datatable列,并将数据类型设置为Active is boolean。@WaelAbbas,感谢您的宝贵意见。我已经更新了我的答案。请看一看。这个答案很好,但我忽略了一个重要的细节,那就是我不能使用预定义的对象。我正在编写的应用程序的目的是接收任何CSV文件并将其解析为数据表。我正在更新我的问题accordingly@DrydenLong,我认为,这不能用CsvDataReader完成。请阅读github论坛以了解报告的问题:以及Ivan Stoev answer。要重现该问题,您必须手动添加datatable列,并为其指定数据类型,如Active is boolean。@WaelAbbas,感谢您的宝贵意见。我已经更新了我的答案。请看一看。这个答案很好,但我忽略了一个重要的细节,那就是我不能使用预定义的对象。我正在编写的应用程序的目的是接收任何CSV文件并将其解析为数据表。我正在更新我的问题accordingly@DrydenLong,我认为,这不能用CsvDataReader完成。请阅读github论坛,了解报道的问题:以及Ivan Stoev的答案。这非常完美,比我希望的要复杂一些,但正是我需要的结果。我只是浪费了2个小时,没有吃午饭,因为CsvDataReader忽略了CsvReader的配置…@PanagiotisKanavos:这非常完美,比我希望的要复杂一点,但这正是我需要的结果。我只是浪费了2个小时,没有吃午饭,因为CsvDataReader忽略了CsvReader的配置…@PanagiotisKanavos: