コレクションをクリアしようとすると(.Clear
を呼び出す)、次の例外が発生します。
関係の外部キープロパティを公開しないエンティティの保存中にエラーが発生しました。 EntityEntriesプロパティはnullを返します。これは、単一のエンティティを例外のソースとして識別できないためです。保存中の例外の処理は、エンティティタイプの外部キープロパティを公開することで簡単になります。詳細については、InnerExceptionを参照してください。
内部の例外は次のとおりです。
「User_Availability」AssociationSetの関係は「削除済み」状態です。多重度の制約がある場合、対応する「User_Availability_Target」も「削除済み」状態である必要があります。
ユーザーは次のようになります。
....
ICollection<Availability> Availability { get; set; }
可用性は次のようになります。
int ID { get; set; }
User User { get; set; }
DateTime Start { get; set;
DateTime End { get; set; }
構成は次のとおりです。
HasMany(x => x.Availability).WithRequired(x => x.User);
HasRequired(x => x.User).WithMany(x => x.Availability);
問題の原因となっているコードは次のとおりです。
user.Availability.Clear();
DbSetを使用してアイテムを削除するなど、他の方法も検討しましたが、コードがクリーンになるとは思いません。コレクションをクリアしてこれを達成する方法はありますか?
それを機能させるために私が知っている唯一の方法は、関係を 識別関係 として定義することです。モデルに外部キーとしてAvailability
からUser
までの外部キーを導入する必要があります...
_public int ID { get; set; }
public int UserID { get; set; }
public User User { get; set; }
_
...そしてそれを主キーの一部にします:
_modelBuilder.Entity<Availability>()
.HasKey(a => new { a.ID, a.UserID });
_
マッピングを拡張してこの外部キーを含めることができます(明示的にするために、EFは慣例によりそれを認識するため、これは必要ありません)。
_modelBuilder.Entity<Availability>()
.HasRequired(a => a.User)
.WithMany(u => u.Availability)
.HasForeignKey(a => a.UserID);
_
(ところで、関係を設定する必要があるのは片側だけです。質問でこれらの両方のマッピングを持つ必要はありません。)
これで、user.Availability.Clear();
を使用してコレクションをクリアでき、Availability
エンティティがデータベースから削除されます。
1つのトリックがあります。特別なDbSetを使用せずにエンティティを削除できます。
(this.dataContext as IObjectContextAdapter).ObjectContext.DeleteObject(entity);
クリアする前に、可用性コレクションの各アイテムに対してこれを実行します。この方法では、「関係を特定する」必要はありません。
SQLiteを使用して同じ問題が発生した場合:
残念ながら、SQLiteは複合キーの自動インクリメントをサポートしていないため、受け入れられた回答はSQLiteでは機能しません。
また、データベースコンテキストのSaveChanges()メソッドをオーバーライドして、子を削除することもできます。
//// Long Version
//var localChilds = this.SubCategories.Local.ToList();
//var deletedChilds = localChilds.Where(w => w.Category == null).ToList();
//foreach(var child in deletedChilds) {
// this.SubCategories.Remove(child);
//}
// Short in LINQ
this.SubCategories.Local
.Where(w => w.Category == null).ToList()
.ForEach(fe => this.SubCategories.Remove(fe));
#endregion
私のソースとして this great Blogpostを参照してください(残念ながらドイツ語で書かれています)。