多対多エンティティフレームワークの挿入/更新。どうすればいいのですか?


104

私はEF4を使用していますが、これは初めてです。私のプロジェクトには多対多があり、挿入または更新の方法を理解できないようです。コード化する方法を確認するためだけに、小さなプロジェクトを作成しました。

テーブルが3つあるとします

  1. クラス:ClassID-ClassName
  2. 学生:StudentID-FirstName-Surname
  3. StudentClass:StudentID-ClassID

すべての関係を追加し、モデルブラウザを介してモデルを更新した後、StudentClassが表示されないことに気付きました。これはデフォルトの動作のようです。

次に、挿入と更新の両方を行う必要があります。どうやってやるの?サンプルをダウンロードできるコードサンプルやリンクはありますか、それとも5分の余裕がありますか?

回答:


139

エンティティ(またはオブジェクト)に関してClassは、のコレクションを持つオブジェクトとのコレクションをStudents持つStudentオブジェクトがありClassesます。あなたのでStudentClass、テーブルはIDのみと余分な情報が含まれている、EFは、加入テーブルのエンティティを生成しません。それが正しい動作であり、それはあなたが期待することです。

ここで、挿入または更新を行うときは、オブジェクトの観点から考えてみてください。たとえば、2人の生徒がいるクラスを挿入するClass場合は、Studentオブジェクトを作成し、オブジェクトをクラスのStudentsコレクションに追加して、Classオブジェクトをコンテキストに追加して、次のように呼び出しますSaveChanges

using (var context = new YourContext())
{
    var mathClass = new Class { Name = "Math" };
    mathClass.Students.Add(new Student { Name = "Alice" });
    mathClass.Students.Add(new Student { Name = "Bob" });

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

これにより、Classテーブルに1つのエントリ、Studentテーブルに2つのエントリ、StudentClassそれらをリンクするテーブルに2つのエントリが作成されます。

あなたは基本的にアップデートに対して同じことをします。データをフェッチし、コレクションにオブジェクトを追加および削除してグラフを変更し、を呼び出しますSaveChanges。詳細については、この同様の質問を確認してください。

編集

あなたのコメントによると、あなたは新しいものを挿入しClass、それに2つの既存のものStudentsを追加する必要があります:

using (var context = new YourContext())
{
    var mathClass= new Class { Name = "Math" };
    Student student1 = context.Students.FirstOrDefault(s => s.Name == "Alice");
    Student student2 = context.Students.FirstOrDefault(s => s.Name == "Bob");
    mathClass.Students.Add(student1);
    mathClass.Students.Add(student2);

    context.AddToClasses(mathClass);
    context.SaveChanges();
}

両方の生徒がすでにデータベースにあるので、それらは挿入されませんが、現在のStudentsコレクションにあるのでClass、2つのエントリがStudentClassテーブルに挿入されます。


こんにちは、あなたのreply.Myシナリオのためのおかげで、私はどのように私はクラスに1つのエントリを挿入する必要があることですし、あなたの例xとStudentClassとStudent.Iへの無エントリに2つのエントリが混乱していますその
user9969

それはうまくいきました。更新に関して、私がする必要がある何か特別なことはありますか?
user9969 2010年

3
これにより、追加する必要があるアイテムをデータベースからフェッチするオーバーヘッドが追加されます。Attachメソッドは、リレーションのみを追加するために使用できます。msdn.microsoft.com/en-us/data/jj592676.aspxおよびstackoverflow.com/questions/11355019/…を
Gnomo

3
AddToClassesはクラスのDbSetですか?
Jo Smo、2016

1
ClassとStudentのコンストラクタでコレクションを初期化することを忘れないでください。例:public Class(){this.Students = new HashSet <Student>(); }
user1040323

40

更新のためにこれを試してください:

[HttpPost]
public ActionResult Edit(Models.MathClass mathClassModel)
{
    //get current entry from db (db is context)
    var item = db.Entry<Models.MathClass>(mathClassModel);

    //change item state to modified
    item.State = System.Data.Entity.EntityState.Modified;

    //load existing items for ManyToMany collection
    item.Collection(i => i.Students).Load();

    //clear Student items          
    mathClassModel.Students.Clear();

    //add Toner items
    foreach (var studentId in mathClassModel.SelectedStudents)
    {
        var student = db.Student.Find(int.Parse(studentId));
        mathClassModel.Students.Add(student);
    }                

    if (ModelState.IsValid)
    {
       db.SaveChanges();
       return RedirectToAction("Index");
    }

    return View(mathClassModel);
}

これは私の日を救ってくれてありがとう!!! それらは、item.Collection(i => i.Students).Load();である重要な部分です。パート
Gerrie Pretorius

余談ですが、その際にInvalidOperationExceptionが発生しました。エンティティのようなものがコンテキストに存在しませんでした。それを処理するために、context.MyEntityCollection.Attach(myEntity)を呼び出しました。
Kilazur、2015

それを理解することは決してなかったでしょう。トンありがとう。
Jared Beach

1
ここに感謝を込めます。このタスクを達成するために、過去4日間で130行を超えるコードを記述してテストしましたが、実行できませんでした。これを試したところ、物事は完全に機能しました。item変数を設定する目的がわからないので、まったく使用しませんが、機能することを嬉しく思います。百万バジリオンありがとう!!!! :)
Dude-Dastic

質問が挿入と更新であることを考えると、この回答は質問と承認された回答を完成させます。どうもありがとうございました!
Vahx

5

私の経験を追加したかったのです。実際にEFでは、オブジェクトをコンテキストに追加すると、すべての子と関連エンティティの状態が「追加済み」に変更されます。ここのルールには小さな例外がありますが、子/関連エンティティが同じコンテキストで追跡されている場合、EFはこれらのエンティティが存在することを理解し、それらを追加しません。この問題は、たとえば、他のコンテキストまたはWeb UIなどから子/関連エンティティをロードしたときに発生します。そうすると、EFはこれらのエンティティについて何も認識せず、すべてのエンティティを追加します。これを回避するには、エンティティのキ​​ーを取得してそれらを見つけます(たとえばcontext.Students.FirstOrDefault(s => s.Name == "Alice"))、追加を行うのと同じコンテキストで)。


3

外部キーのみが関係する多対多の関係を処理するには、次の方法を使用します。

だから挿入するために:

public void InsertStudentClass (long studentId, long classId)
{
    using (var context = new DatabaseContext())
    {
        Student student = new Student { StudentID = studentId };
        context.Students.Add(student);
        context.Students.Attach(student);

        Class class = new Class { ClassID = classId };
        context.Classes.Add(class);
        context.Classes.Attach(class);

        student.Classes = new List<Class>();
        student.Classes.Add(class);

        context.SaveChanges();
    }
}

削除するには

public void DeleteStudentClass(long studentId, long classId)
{
    Student student = context.Students.Include(x => x.Classes).Single(x => x.StudentID == studentId);

    using (var context = new DatabaseContext())
    {
        context.Students.Attach(student);
        Class classToDelete = student.Classes.Find(x => x.ClassID == classId);
        if (classToDelete != null)
        {
            student.Classes.Remove(classToDelete);
            context.SaveChanges();
        }
    }
}

1

エンティティフレームワークでは、オブジェクトがコンテキストに追加されると、その状態が「追加済み」に変わります。EFは各オブジェクトの状態もオブジェクトツリーに追加されるように変更するため、主キー違反エラーが発生するか、テーブルに重複したレコードが追加されます。


2
ユーザーが回答を得るためにブログにアクセスする必要がないように、十分な詳細を提供するように編集してください。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.