私はEF4を使用していますが、新しいものです。私のプロジェクトには多対多があり、挿入または更新の方法がうまくいかないようです。私はそれがどのようにコーディングされるべきかを見るためだけに小さなプロジェクトを構築しました。
3つのテーブルがあるとします
すべての関係を追加し、モデルブラウザでモデルを更新した後、StudentClassが表示されないことに気付きました。これはデフォルトの動作のようです。
次に、挿入と更新の両方を行う必要があります。どうやってやるの?サンプルをダウンロードできるコードサンプルまたはリンク、または5分間の余裕はありますか?
エンティティ(またはオブジェクト)に関しては、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
テーブルにエントリ、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
テーブルに挿入されます。
更新のためにこれを試してください:
[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);
}
その上で私の経験を加えたかった。実際、EFは、オブジェクトをコンテキストに追加すると、すべての子および関連エンティティの状態を「追加済み」に変更します。ここでのルールには小さな例外があります:子/関連エンティティが同じコンテキストで追跡されている場合、EFはこれらのエンティティが存在することを理解し、それらを追加しません。この問題は、たとえば、他のコンテキストまたはWeb UIなどから子/関連エンティティをロードしたときに発生します。そうすると、EFはこれらのエンティティについて何も認識せず、それらすべてを追加します。これを回避するには、エンティティのキーを取得して検索します(たとえば、追加を行うコンテキストと同じコンテキストでcontext.Students.FirstOrDefault(s => s.Name == "Alice"))
.
次の方法を使用して、外部キーのみが関係する多対多の関係を処理します。
挿入の場合:
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();
}
}
}
エンティティフレームワークでは、オブジェクトがコンテキストに追加されると、その状態が[追加済み]に変わります。また、EFは各オブジェクトの状態をオブジェクトツリーに追加するため、主キー違反エラーが発生するか、テーブルに重複レコードが追加されます。