「シーケンスに複数のエンティティが含まれています」というエラーが表示されたアプリケーション。これは通常、Linqの.SingleOrDefault()コマンドの結果であることがわかっているので、調査を開始しました。本番サーバーに重複するキーワードのインスタンスが多数あることを確認できるので、そこから始めます。
次のキーワードテーブルがあります。
- id INT(NOT NULL、PRIMARY KEY)
- テキストNVARCHAR(512)
- アクティブINT
アクティブは、必要に応じてデータを「有効/無効」にする方法にすぎません。LINQ to SQLを使用していて、次のメソッドを実装しています。
public Keyword GetKeyword(String keywordText)
{
return db.Keywords.SingleOrDefault(k => (k.text.ToUpper() == keywordText.ToUpper()));
}
複数のオブジェクトが同じキーワードを参照できるように、関連付けテーブルを介してキーワードを添付するという考え方です。キーワードテーブル内に重複するテキストエントリがあってはなりません。これはデータベースではなく、コードを介して強制されます。これはベストプラクティスではないかもしれませんが、現時点での私の問題の中で最も少ないものです。したがって、オブジェクトを作成するときは、次のようにします。
Keyword keyword = GetKeyword(keywordText)
if(keyword == null)
{
keyword = new Keyword();
keyword.text = keywordText;
keyword.active = Globals.ACTIVE;
db.Keywords.InsertOnSubmit(keyword);
}
KeywordReference reference = new KeywordReference();
reference.keywordId = keyword.id;
myObject.KeywordReferences.Add(reference);
db.SubmitChanges();
このコードは実際には言い換えられています。私はリポジトリパターンを使用しているため、関連するすべてのコードははるかに長くなります。ただし、広範囲にわたってテストしたため、コードが意図したとおりに機能していることを保証できます。問題はデータベースレベルで発生しているようです。
そのため、テストデータベースでいくつかのテストと手動クエリを実行し、テストで合格したキーワードがデータベースにないことを確認しましたが、JOINを実行すると、データベースに含まれていることがわかります。だから私はもう少し深く掘り下げて、リスト全体を手動でスキャンすることを選びます:
SELECT * FROM Keyword
737件の結果を返します。それらすべてを調べたくないと思ったので、テキストで注文して737件の結果を取得しました。最近追加した単語を探しましたが、リストに表示されません。
混乱して、最近作業したオブジェクトに関連付けられているすべてのkeywordIdを検索すると、IDが1,000を超えるものがいくつか表示されます(idは1ずつ自動インクリメントされるように設定されています)。キーワードを実際に削除することはないので(したがって「アクティブ」列)、1から少なくとも1,000を超える数までのすべてのIDが存在する必要があることを知って、別のクエリを実行します。
SELECT * FROM Keyword ORDER BY id
737の結果を返し、最大IDは737で停止します。
SELECT * FROM Keyword ORDER BY id DESC
1308行を返します
主キーまたは一意の識別子がない場合、このような不一致を以前に見ましたが、id列が実際には両方であることを確認しました。ここからどこに行くのかよくわかりません。また、本番環境で約4,000以上のキーワードが重複していて、それぞれの異なるインスタンスを参照するオブジェクトがさらにいくつかあるという追加の問題があります。