CSVHelper - using two mapping classes and choosing the mapping based on the value in a field of each row of the csv file

安稳与你 提交于 2020-01-06 08:14:46

问题


Using the CSVHelper .NET library with .NET Core 2.2 to parse large csv file (over 1 million rows) and write it to a SQL Server table.

I have two mapping classes: we need to loop through each row, and if the first value of the row is 1, we need to use class map 1, and if the value is 2, we need to use class map 2. Because CSVHelper is geared toward doing this activity in bulk, I'm having trouble conceptualizing how to use the if statement and for each loop to accomplish this task.

This what I have so far:

SQL Entity Class

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

CSVHelper Mapping Class 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 Mapping Class 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 (this is what we had before being given the requirement for the conditional mapping)

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();
    }
}

As the above example of the Program.cs code demonstrates the typical ease-of-use scenario which CSVHelper seems to be designed for, I'm having difficulty conceptualizing how to use two class maps, and how to loop through each row of the csv file and choose the class map depending on the value in a column in a given row.


回答1:


When I original heard your requirements, I thought you just need to check for the ClassMap once per document. It now sounds like it will change per row. Unfortunately, I have found once you register and start using a ClassMap it appears to get cached and you can't switch it half way through, so I don't think the two ClassMaps will work. I'll give you a couple of ways to solve it and you can let me know if either satisfies your requirements.

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")
            );
    }
}

Another option would be to manually construct the TaskEntity objects.

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.
}


来源:https://stackoverflow.com/questions/58190364/csvhelper-using-two-mapping-classes-and-choosing-the-mapping-based-on-the-valu

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!