插入/更新多对多实体框架。我该怎么做?


104

我正在使用EF4和它的新功能。我的项目中有很多很多,似乎无法弄清楚如何插入或更新。我建立了一个小项目,只是为了看看应该如何编码。

假设我有3张桌子

  1. 类别:ClassID-ClassName
  2. 学生:StudentID-名字-姓氏
  3. StudentClass:StudentID-ClassID

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

现在,我需要同时进行插入和更新。你怎么做呢?我可以下载示例的任何代码示例或链接,还是可以节省5分钟?

Answers:


139

就实体(或对象)而言,您有一个Class对象的集合,StudentsStudent对象的集合和一个对象的集合Classes。由于您的StudentClass表仅包含ID,而没有其他信息,因此EF不会为联接表生成实体。这是正确的行为,这就是您的期望。

现在,在进行插入或更新时,请尝试根据对象进行思考。例如,如果您想插入一个有两个学生的课程,创建一个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表中的两个条目StudentClass将它们链接在一起。

基本上,您也需要进行相同的更新。只需获取数据,即可通过在集合中添加和删除对象来修改图形,请调用SaveChanges。查看此类似问题以获取详细信息。

编辑

根据您的评论,您需要插入一个新的Class并添加两个现有的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会将两个条目插入StudentClass表中。


您好,感谢您的reply.My的情况是,我需要插入1次进入类,并为x你的榜样2项进入StudentClass并没有进入Student.I很困惑我该怎么办
user9969

进行了处理。关于更新。我需要做些特别的事情吗?例如,只需更新className。
user9969

3
这将增加从数据库中获取需要添加的项目的开销。Attach方法只能用于添加关系。见msdn.microsoft.com/en-us/data/jj592676.aspxstackoverflow.com/questions/11355019/...
Gnomo

3
AddToClasses是类的DbSet吗?
Jo Smo

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 2014年

只是附带说明,这样做时我有一个InvalidOperationException,类似我的实体的内容在上下文中不存在。我只是调用context.MyEntityCollection.Attach(myEntity)来处理它。
Kilazur

永远不会弄清楚这一点。万分感谢。
贾里德海滩

1
请允许我在这里添加我的感谢。在过去的4天里,我编写并测试了130余行代码,试图完成此任务,但无法完成。尝试了一下,一切正常。虽然我不明白建立item变量然后根本不使用它的目的,但我很高兴它能起作用!谢谢一百万亿!:)
Dude-Dastic's

考虑到问题是“插入并更新”,此答案将完成问题并接受答案。非常感谢你!
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
请进行编辑以提供足够的详细信息,以便用户不必访问您的博客即可获得答案。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.