我正在使用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文件中的每条线路或游戏创建两个新的团队对象。
我真的不确定如何使用映射来完成这项工作,欢迎提供帮助。
提前谢谢。
答案 0 :(得分:2)
我查看了CsvHelper
库及其内部对象构造逻辑。
虽然你可以使用聪明的配置技巧,但我发现做你想要的最好的方法很简单。您只需跟踪唯一的Referee
和Team
,并在可用时将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);
}
}