テレビ番組フォルダーを含むディレクトリをスキャンし、tvrage API を使用して番組の詳細を検索し、エンティティ フレームワークを使用して詳細をデータベースに保存するプログラムを作成しようとしています。
TVShow テーブルの pkey は、tvrage データベースの番組 ID から取得したものと同じ値であり、重複または類似のフォルダ名が同じ番組情報を返すと問題が発生します。「Alias」、「Alias 1」、「Band of Brothers」の 3 つのフォルダーを含むディレクトリがある状況では、コードから次の出力が得られます。
* テレビ番組 *
エイリアス......一致なし......追加中....完了
エイリアス 1 ......一致なし.....追加中....追加できません。ID はすでに DB に存在します
バンド・オブ・ブラザーズ ...... NO MATCH..ADDING....
context.SaveChanges(); で UpdateException を取得する前に。行 PRIMARY KEY 制約 'PK_TVShows' に違反しています。
SQL プロファイラーを使用すると、アプリがエイリアス ショーで重複キーを使用して 2 回目の挿入を実行しようとしていることが問題であることがわかりますが、その理由はわかりません。foreach ループ (2 番目の「エイリアス」フォルダー) の 2 番目の対話でコードをステップ実行すると、ショー エンティティをデータベースに保存するコードがバイパスされます。
「バンド オブ ブラザーズ」の新しい TVShow エンティティを作成したときに、foreach ループの次の繰り返しでのみ、Tvshow をコンテキストに追加して保存するコードに実際に到達し、その時点でアプリがクラッシュします。ビジュアルスタジオでは、クラッシュの時点で次のことがわかります。
- context.TVShows.AddObject(show) 内の「show」エンティティは、一意の ID を持つ「バンド オブ ブラザーズ」です。
- context.TVShows には、最初のエイリアス エンティティである 1 つのレコードのみが含まれます。
しかし、SQL プロファイラーは、EntityFramework が代わりに 2 回目の Alias を挿入していることを示しています。
private void ScanForTVShowFolders( GenreDirectoryInfo drive ) {
IEnumerable<DirectoryInfo> shows = drive.DirInfo.EnumerateDirectories();
foreach (DirectoryInfo d in shows) {
//showList contains a list of existing TV show names previously queried out of DB
if (showList.Contains(d.Name)) {
System.Console.WriteLine(d.Name + ".....MATCH");
} else {
System.Console.Write(d.Name + "......NO MATCH..ADDING....");
TVShow show = LookUpShowOnline(d.Name, drive.GenreName);
if (show.Id == -1) { // id of -1 means online search failed
System.Console.Write("..........CANT FIND SHOW" + Environment.NewLine);
} else if (context.TVShows.Any(a => a.Id == show.Id)) { //catch duplicate primary key insert
System.Console.Write(".......CANT ADD, ID ALREADY EXISTS IN DB" + Environment.NewLine);
} else {
context.TVShows.AddObject(show);
context.SaveChanges();
System.Console.Write("....DONE" + Environment.NewLine);
}
}
}
private TVShow LookUpShowOnline( string name, string genre ) {
string xmlPath = String.Format("http://services.tvrage.com/feeds/search.php?show='{0}'", name);
TVShow aShow = new TVShow();
aShow.Id = -1; // -1 = Can't find
XmlDocument xmlResp = new XmlDocument();
try { xmlResp.Load(xmlPath); } catch (WebException e) { System.Console.WriteLine(e); }
XmlNode root = xmlResp.FirstChild;
if (root.NodeType == XmlNodeType.XmlDeclaration) { root = root.NextSibling; }
XmlNode tvShowXML;
//if (showXML["episode"] == null)
// return false;
tvShowXML = root["show"];
if (tvShowXML != null) {
aShow.Id = System.Convert.ToInt16(tvShowXML["showid"].InnerText);
aShow.Name = tvShowXML["name"].InnerText.Trim();
aShow.StartYear = tvShowXML["started"].InnerText.Trim();
aShow.Status = tvShowXML["status"].InnerText.Trim();
aShow.TVGenre = context.TVGenres.Where(b => b.Name.Trim() == genre).Single();
}
return aShow;
}
}
編集 さらに読んで、context.ObjectStateManager をデバッグ ウォッチリストに追加しました。新しい TVShow エンティティを作成するたびに、新しいレコードが _addedEntityStore に追加されることがわかります。実際に context.TVShows.AddObject(show) を削除しても、コードはデータベースを更新するため、コンテキストに手動で追加するのは冗長に思えます。