插入/更新多对多实体框架。我如何做到这一点?

我用的是 EF4,而且是新手。我在我的项目中有许多对许多,似乎不能工作出如何插入或更新。我已经建立了一个小项目,只是为了看看它应该如何编码。

假设我有三张桌子

  1. 类别: ClassID-ClassName
  2. 学生: StudentID-FirstName-姓氏
  3. Student Class: StudentID-ClassID

在添加了所有关系并通过模型浏览器更新了模型之后,我注意到 StudentClass 没有出现,这似乎是默认行为。

现在我需要同时执行 Insert 和 Update。你是怎么做到的?任何代码样本或链接,我可以下载一个例子,或者你能腾出5分钟?

138326 次浏览

In terms of entities (or objects) you have a Class object which has a collection of Students and a Student object that has a collection of Classes. Since your StudentClass table only contains the Ids and no extra information, EF does not generate an entity for the joining table. That is the correct behaviour and that's what you expect.

Now, when doing inserts or updates, try to think in terms of objects. E.g. if you want to insert a class with two students, create the Class object, the Student objects, add the students to the class Students collection add the Class object to the context and call 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();
}

This will create an entry in the Class table, two entries in the Student table and two entries in the StudentClass table linking them together.

You basically do the same for updates. Just fetch the data, modify the graph by adding and removing objects from collections, call SaveChanges. Check this similar question for details.

Edit:

According to your comment, you need to insert a new Class and add two existing Students to it:

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();
}

Since both students are already in the database, they won't be inserted, but since they are now in the Students collection of the Class, two entries will be inserted into the StudentClass table.

In entity framework, when object is added to context, its state changes to Added. EF also changes state of each object to added in object tree and hence you are either getting primary key violation error or duplicate records are added in table.

Try this one for Updating:

[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);
}

I wanted to add my experience on that. Indeed EF, when you add an object to the context, it changes the state of all the children and related entities to Added. Although there is a small exception in the rule here: if the children/related entities are being tracked by the same context, EF does understand that these entities exist and doesn't add them. The problem happens when for example, you load the children/related entities from some other context or a web ui etc and then yes, EF doesn't know anything about these entities and goes and adds all of them. To avoid that, just get the keys of the entities and find them (e.g. context.Students.FirstOrDefault(s => s.Name == "Alice")) in the same context in which you want to do the addition.

I use the following way to handle the many-to-many relationship where only foreign keys are involved.

So for inserting:

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();
}
}

For deleting,

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();
}
}
}