csvHelper动态加载子类

时间:2016-05-03 12:24:15

标签: c# c#-4.0 csvhelper

我正在使用csvHelper从.CSV文件填充数据库。从.csv文件中读取的任何记录都有3个子类,这些子类中的任何一个可能已经存在,也可能不存在于数据库中。

我的问题是,csvHelper为每个存在的子类创建一个新记录,而不是它应该查找数据库中的现有记录,如果存在则使用它。结果是,我最终在子类表中有许多重复的条目。

我正在使用带有工作单元方法的Unity容器。

这是我的代码

class Migration(migrations.Migration):

    ...

    operations = [
        migrations.RunSQL('ALTER TABLE __your_table__ ENGINE=MYISAM;')
    ]

这是我的班级地图

 public class Game {    
     public Referee Referee { get; set; } 
     public Team HomeTeam { get; set; }
     public Team AwayTeam { get; set; }
 }

 public class Referee {
    public string Name { get; set; }
 }

 public class Team {
     public string Name { get; set; }   
 }

我通常会使用CSV作为Stream资源来访问CSV文件,如下所示:

 public sealed class GameMap : CsvClassMap<Game> {
    public GameMap ()        {
        References<RefereeMap>(m => m.Referee);
         References<HomeTeamMap>(m => m.HomeTeam);
         References<AwayTeamMap>(m => m.AwayTeam);
    }
 }

 public sealed class RefereeMap : CsvClassMap<Referee> {
    public RefereeMap ()        {
        Map(m => m.Name).Name("RefereeName");
    }
 }

 public sealed class HomeTeamMap : CsvClassMap<Team> {
    public HomeTeamMap ()       {
        Map(m => m.Name).Name("TeamName");
    }
 }

 public sealed class AwayTeamMap : CsvClassMap<Team> {
    public AwayTeamMap ()       {
        Map(m => m.Name).Name("TeamName");
    }
 }

CSV文件中的几行通常看起来像这样

byte[] byteData = webClient.DownloadData(uriAddress);
Stream byteStream = new MemoryStream(byteData);
TextReader reader = new StreamReader(byteStream);

var csv = new CsvReader( reader );
csv.Configuration.RegisterClassMap<GameMap>();
csv.Configuration.RegisterClassMap<RefereeMap>();
csv.Configuration.RegisterClassMap<HomeTeamMap>();
csv.Configuration.RegisterClassMap<AwayTeamMap>();

var records = new List<Game>();
while (csv.Read())
{
     records.Add(csv.GetRecord<Game>());
}
...

在此示例中,将创建总共4个游戏,创建8个团队和4个裁判。莱斯特,埃弗顿,史蒂夫邓恩和安迪霍尔都将包含重复,这是不正确的,即莱斯特的三个团队对象和安迪霍尔等的2个裁判对象。

我正在使用的.CSV文件采用平面格式,每个游戏都有一行。主队,客队和裁判都有专栏。还有其他专栏,但出于我要问的问题的目的,其他细节是无关紧要的。

当csvHelper读取游戏记录时,它会创建1个新裁判和2个新团队。在阅读了300场比赛之后,数据库中有300名裁判(250多个重复)和600个队(550多个重复)。理想情况下,在创建新裁判员和队伍之前,应首先从数据库中查找裁判和团队。

由于每场比赛有两支球队,因此会为CSV文件中的每条线路或游戏创建两个新的团队对象。

我真的不确定如何使用映射来完成这项工作,欢迎提供帮助。

提前谢谢。

1 个答案:

答案 0 :(得分:2)

我查看了CsvHelper库及其内部对象构造逻辑。

虽然你可以使用聪明的配置技巧,但我发现做你想要的最好的方法很简单。您只需跟踪唯一的RefereeTeam,并在可用时将Game个实例链接到之前的实例。

public static class SetExtensions {
    public static TValue GetExistingOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> set, TKey key, TValue value) {
        TValue existing;
        if (set.TryGetValue(key, out existing)) {
            return existing;
        }
        set.Add(key, value);
        return value;
    }
}

class Program {
    static void Main(string[] args) {
        Stream inputStream = new MemoryStream();
        using (var sw = new StreamWriter(inputStream,  Encoding.UTF8, 4096, true)) {
            sw.WriteLine("Home,      Away,       Referee");
            sw.WriteLine("Leeds,     Leicester,  Steve Dunn");
            sw.WriteLine("Derby,     Everton,    Steve Dunn");
            sw.WriteLine("Leicester, Man United, Andy Hall");
            sw.WriteLine("Everton,   Leicester,  Andy Hall");
        }
        inputStream.Position = 0;

        TextReader reader = new StreamReader(inputStream);

        var csv = new CsvReader(reader);
        csv.Configuration.TrimFields = true;
        csv.Configuration.TrimHeaders = true;
        csv.Configuration.RegisterClassMap<GameMap>(); //You only need to register the "root" map

        var referees = new Dictionary<string, Referee>(); //Stores unique referees. You can use the full Referee object as a key if you implement IEquatable<Referee> for Referee
        var teams = new Dictionary<string, Team>();
        var records = new List<Game>();
        while (csv.Read()) {
            var record = csv.GetRecord<Game>();
            record.Referee = referees.GetExistingOrAdd(record.Referee.Name, record.Referee); //Try to link to existing object
            record.HomeTeam = teams.GetExistingOrAdd(record.HomeTeam.Name, record.HomeTeam); //Try to link to existing object
            record.AwayTeam = teams.GetExistingOrAdd(record.AwayTeam.Name, record.AwayTeam); //Try to link to existing object

            records.Add(record);
        }
    }