RelayConfigとStandardContactの 2 つのエンティティ間に多対多の関係があります。
エンティティ:
public class RelayConfig : EntityBase, IDataErrorInfo {
...
//Associations
public virtual ICollection<StandardContact> StandardContacts { get; set; }
}
public class StandardContact :EntityBase, IDataErrorInfo {
...
//Associations
public virtual ICollection<RelayConfig> RelayConfigs { get; set; }
}
現在、RelayConfig とその StandardContact との関係を更新しようとしています。RelayConfig を更新するコードは次のとおりです。
public class RelayConfigRepository : GenericRepository<RelayConfig> {
....
public void Update(RelayConfig relayConfig, List<StandardContact> addedContacts, List<StandardContact> deletedContacts) {
context.RelayConfigs.Add(relayConfig);
if (relayConfig.Id > 0) {
context.Entry(relayConfig).State = EntityState.Modified;
}
addedContacts.ForEach(ad => relayConfig.StandardContacts.Add(ad));
foreach (StandardContact standardContact in relayConfig.StandardContacts) {
if (standardContact.Id > 0) {
context.Entry(standardContact).State = EntityState.Modified;
}
}
relayConfig.StandardContacts.ToList().ForEach(s => {
if (deletedContacts.Any(ds => ds.Id == s.Id)) {
context.Entry(s).State = EntityState.Deleted;
}
});
}
...
}
更新を実行すると、例外が発生します。その内部例外を以下に示します。
InnerException: System.Data.SqlClient.SqlException
Message=Violation of PRIMARY KEY constraint 'PK__Standard__EE33D91D1A14E395'. Cannot insert duplicate key in object 'dbo.StandardContactRelayConfigs'.
dbo.StandardContactRelayConfigs は、RelayConfig と StandardContact をリンクするリンク テーブルです。ご覧のとおり、Id > 0 の場合、Update コードはすべてのエンティティを変更済み状態に変更します (Update メソッドの最後に設定された削除済みレコードを除く)。
エンティティ フレームワークがリンク テーブルに行を挿入しようとして、上記の例外で失敗する理由を本当に理解できません。既存の RelayConfig.StandardContacts エンティティの EntityState を既に Modified に変更しています。
要するに、なぜ上に貼り付けられた例外が発生するのですか。
よろしく、ニルヴァン。
編集: 上記の Update メソッドのパラメーター (addedContacts および deletedContacts) は、Id > 0 の既存のエンティティです。
Edit2: あなたの提案に従って、更新メソッドから新しい(データベースに存在しない)レコードを挿入するためのコードを削除しました。したがって、私の update メソッドは、既存の StandardContact レコードのみを RelayConfig コレクションに追加します。しかし、まだコードを正しく動作させることができません。まず、ここに私が使用しているコードがあります
public void Update(RelayConfig relayConfig, List<StandardContact> addedContacts, List<StandardContact> deletedContacts) {
context.RelayConfigs.Add(relayConfig);
if (relayConfig.Id > 0) {
context.Entry(relayConfig).State = EntityState.Modified;
}
addedContacts.ForEach(contact => {
context.StandardContacts.Attach(contact);
relayConfig.StandardContacts.Add(contact);
objectContext.ObjectStateManager.
ChangeRelationshipState(relayConfig, contact, rs => rs.StandardContacts, EntityState.Added);
});
}
今のところ、私は追加されたレコードに集中しています。上記のコードは、StandardContact (contact 変数) が他の既存の RelayConfig オブジェクトと何の関係も持たない場合にうまく機能します。その場合、RelayConfig.StandardContacts コレクションに追加された連絡先ごとに、ジャンクション テーブルに新しいエントリが作成されます。しかし、StandardContact (contact 変数) が既に他の RelayConfig オブジェクトと関係を持っている場合、事態は見苦しくなります (予期しない動作)。その場合、StandardContact が RelayConfig.StandardContacts コレクションに追加されると、StandardContact もデータベースに追加されるため、重複したエントリが作成されます。それだけでなく、新しい RelayConfig オブジェクトも作成され (どこからかはわかりません)、RelayConfigs テーブルに挿入されます。
@Ladislav、多対多の関係の更新 (切り離されたエンティティの場合) で機能するサンプル コードがある場合は、同じものを見せてください。
よろしく、 ニルヴァン
Edit3(ソリューション):
最終的には、まったく異なるアプローチを使用することになりました。これがアップデートのコードです
public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {
context.Entry(relayConfig).State = EntityState.Modified;
relayConfig.StandardContacts.Clear();
exposedContacts.ForEach(exposedContact => {
StandardContact exposedContactEntity = null;
exposedContactEntity = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
if (exposedContactEntity != null) {
relayConfig.StandardContacts.Add(exposedContactEntity);
}
});
}
よろしく、ニルヴァン。