csvHelper 动态加载子 class

csvHelper dynamically loading child class

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

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

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

这是我的代码

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

这是我的class地图

 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 作为流资源来访问 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>());
}
...

CSV 文件中的几行通常如下所示

Home,      Away,       Referee
Leeds,     Leicester,  Steve Dunn
Derby,     Everton,    Steve Dunn
Leicester, Man United, Andy Hall
Everton,   Leicester,  Andy Hall

在这个例子中,总共创建了4场比赛,创建了8支球队和4名裁判。莱斯特、埃弗顿、史蒂夫邓恩和安迪霍尔都包含重复项,这是不正确的,即莱斯特的三个团队对象和安迪霍尔的 2 个裁判对象等。

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

当 csvHelper 读取比赛记录时,它会创建 1 个新裁判和 2 个新球队。在阅读了 300 场比赛后,数据库中有 300 名裁判(250+ 重复)和 600 支球队(550+ 重复)。理想情况下,应先从数据库中查找裁判和团队,然后再创建新的。

因为每场比赛有两支球队,所以在 CSV 文件中为每一行或每场比赛创建了两个新的球队对象。

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

提前致谢。

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

虽然您可以使用巧妙的配置技巧,但我发现实现您想要的效果的最佳方法非常简单。您只需跟踪唯一的 Referees 和 Teams 和 link 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);
        }
    }