【问题标题】:CSVHelper - using two mapping classes and choosing the mapping based on the value in a field of each row of the csv fileCSVHelper - 使用两个映射类并根据 csv 文件每一行的字段中的值选择映射
【发布时间】:2020-01-31 01:47:44
【问题描述】:

在 .NET Core 2.2 中使用 CSVHelper .NET 库来解析大型 csv 文件(超过 100 万行)并将其写入 SQL Server 表。

我有两个映射类:我们需要循环遍历每一行,如果该行的第一个值为1,我们需要使用类映射1,如果值为2,我们需要使用类映射2 . 因为 CSVHelper 适合批量执行此活动,所以我无法概念化如何使用 if 语句和 for each 循环来完成此任务。

这是我目前所拥有的:

SQL 实体类

public class TaskEntity
{
    public int Id { get; set; }
    public string SqlTableColumn1 { get; set; }
    public string SqlTableColumn2 { get; set; }
}

CSVHelper 映射类 1

public sealed class TaskEntityMap1 : ClassMap<TaskEntity>
{
    public TaskEntityMap1()
    {
        Map(m => m.SqlTableColumn1).Name("CsvColumn1");
        Map(m => m.SqlTableColumn2).ConvertUsing(row => row.GetField<string>("CsvColumn2") + " " + row.GetField<string>("CsvColumn3"));
    }
}

CSVHelper 映射类 2

public sealed class TaskEntityMap2 : ClassMap<TaskEntity>
{
    public TaskEntityMap2()
    {
        Map(m => m.SqlTableColumn1).Name("CsvColumn4");
        Map(m => m.SqlTableColumn2).ConvertUsing(row => row.GetField<string>("CsvColumn5") + " " + row.GetField<string>("CsvColumn6"));
    }
}

Program.cs(这是我们获得条件映射要求之前所拥有的)

public static void Main(string[] args)
{
    using (var reader = new StreamReader(@"C:\Users\me\Documents\file.csv"))
    using (var csv = new CsvReader(reader))          
    {
        csv.Configuration.PrepareHeaderForMatch = (string header, int index) =>
            header.Replace(" ", "_").Replace("(", "").Replace(")", "").Replace(".", "");

        csv.Configuration.RegisterClassMap<TaskEntityMap>();

        var records = csv.GetRecords<TaskEntity>().ToList();
    }
}

由于Program.cs 代码的上述示例演示了 CSVHelper 似乎设计用于的典型易用性场景,我很难概念化如何使用两个类映射,以及如何循环遍历每一行的 csv 文件并根据给定行中的列中的值选择类映射。

【问题讨论】:

    标签: c# csvhelper


    【解决方案1】:

    当我最初听到您的要求时,我以为您只需要在每个文档中检查一次ClassMap。现在听起来它会每行发生变化。不幸的是,我发现一旦你注册并开始使用ClassMap,它似乎会被缓存,你不能在中途切换它,所以我认为这两个ClassMaps 不会起作用。我会给你几种方法来解决它,你可以告诉我是否满足你的要求。

    public static void Main(string[] args)
    {
        using (var reader = new StreamReader(@"C:\Users\me\Documents\file.csv"))
        using (var csv = new CsvReader(reader))          
        {
            csv.Configuration.PrepareHeaderForMatch = (string header, int index) =>
                header.Replace(" ", "_").Replace("(", "").Replace(")", "").Replace(".", "");
    
            csv.Configuration.RegisterClassMap<TaskEntityMap>();
    
            var records = csv.GetRecords<TaskEntity>().ToList();
        }
    }
    
    public sealed class TaskEntityMap : ClassMap<TaskEntity>
    {
        public TaskEntityMap()
        {
            Map(m => m.SqlTableColumn1).ConvertUsing(row => row.GetField<int>(0) == 1 ? 
                row.GetField<string>("CsvColumn1") : 
                row.GetField<string>("CsvColumn4")
                );
            Map(m => m.SqlTableColumn2).ConvertUsing(row => row.GetField<int>(0) == 1 ? 
                row.GetField<string>("CsvColumn2") + " " + row.GetField<string>("CsvColumn3") :
                row.GetField<string>("CsvColumn5") + " " + row.GetField<string>("CsvColumn6")
                );
        }
    }
    

    另一种选择是手动构造TaskEntity 对象。

    public static void Main(string[] args)
    {
        using (var reader = new StreamReader(@"C:\Users\me\Documents\file.csv"))
        using (var csv = new CsvReader(reader))          
        {
            csv.Configuration.PrepareHeaderForMatch = (string header, int index) =>
                header.Replace(" ", "_").Replace("(", "").Replace(")", "").Replace(".", "");
    
            var records = new List<TaskEntity>();
    
            csv.Read();
            csv.ReadHeader();
    
            while (csv.Read())
            {                    
                if (csv.GetField<int>(0) == 1)
                {
                    var record = new TaskEntity
                    {
                        SqlTableColumn1 = csv.GetField<string>("CsvColumn1"),
                        SqlTableColumn2 = csv.GetField<string>("CsvColumn2") + " " + csv.GetField<string>("CsvColumn3")
                    };
                    records.Add(record);
                }
                else
                {
                    var record = new TaskEntity
                    {
                        SqlTableColumn1 = csv.GetField<string>("CsvColumn4"),
                        SqlTableColumn2 = csv.GetField<string>("CsvColumn5") + " " + csv.GetField<string>("CsvColumn6")
                    };
                    records.Add(record);
                }                    
            }
        }  // Break here.
    }
    

    【讨论】:

    • 嗨,大卫。所以看看你的第一个选项,似乎我理论上可以在 lambda 表达式中构建一个 if 条件?我要先试试这个。
    • 是的,事实上,如果您有多种可能性,您可以设置 switch 或 elseif 语句。或者您可以通过其他方法处理并返回该值。
    • 大卫,出于各种原因,我决定尝试您的第二个选项。我正在尝试在 Visual Studio 中调试它(我还没有导出到数据库)并且我无法检查 records 在 VS 中的样子 - 我是否需要添加某种返回声明以便让一些结果显示在任何 VS 调试窗口中,例如 Autos?如果你不知道,我对这一切还是很陌生,所以非常感谢你的帮助!
    • 在调试过程中我没有收到任何错误 - 在我继续执行 SQL 导出之前,我看不到任何表明它工作正常的值的迹象。
    • 也许要确保你没有打破 using 语句?如果我在 using 语句的末尾中断,我可以看到记录。如果在 Main 方法结束时中断,则记录已超出范围。
    猜你喜欢
    • 2022-01-12
    • 2018-03-27
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多