web-dev-qa-db-ja.com

関係は削除済み状態です

コレクションをクリアしようとすると(.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を使用してアイテムを削除するなど、他の方法も検討しましたが、コードがクリーンになるとは思いません。コレクションをクリアしてこれを達成する方法はありますか?

29
Sam

それを機能させるために私が知っている唯一の方法は、関係を 識別関係 として定義することです。モデルに外部キーとして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エンティティがデータベースから削除されます。

31
Slauma

1つのトリックがあります。特別なDbSetを使用せずにエンティティを削除できます。

(this.dataContext as IObjectContextAdapter).ObjectContext.DeleteObject(entity);

クリアする前に、可用性コレクションの各アイテムに対してこれを実行します。この方法では、「関係を特定する」必要はありません。

0
poul_ko

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を参照してください(残念ながらドイツ語で書かれています)。

0
Sven Weber