我们是否需要从db获取对象来创建/更新相关数据?

时间:2017-07-14 01:59:18

标签: asp.net-mvc entity-framework many-to-many

有一篇关于how to update related data with the Entity Framework in an ASP.NET MVC application的文章。它实现了一个简单的大学,您可以选择您的课程作为教师。

以下是其课程控制器的简化版本:

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
   if (selectedCourses == null)
   {
      instructorToUpdate.Courses = new List<Course>();
      return;
   }

   var selectedCoursesHS = new HashSet<string>(selectedCourses);
   var instructorCourses = new HashSet<int>
       (instructorToUpdate.Courses.Select(c => c.CourseID));
   foreach (var course in db.Courses)
   {
      if (selectedCoursesHS.Contains(course.CourseID.ToString()))
      {
         if (!instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Add(course);
         }
      }
      else
      {
         if (instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Remove(course);
         }
      }
   }
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int? id, string[] selectedCourses)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    var instructorToUpdate = db.Instructors
       .Include(i => i.Courses)
       .Where(i => i.ID == id)
       .Single();

    if (TryUpdateModel(instructorToUpdate, "",
       new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    {
        try
        {
            UpdateInstructorCourses(selectedCourses, instructorToUpdate);

            db.SaveChanges();

            return RedirectToAction("Index");
        }
        catch (Exception e)
        {
            //Log the error
        }
    }
    PopulateAssignedCourseData(instructorToUpdate);
    return View(instructorToUpdate);
}

正如您所看到的,UpdateInstructorCourses根据instructorToUpdate.Courses基于字符串数组从db.Courses检索的对象填充selectedCourses

那么,这是创建多对多关系的唯一方法吗?我们是否需要从db然后Add将对象添加到我们的List成员中?仅传递相关对象的ID并更新相关数据不是更好吗?

1 个答案:

答案 0 :(得分:0)

因此,对于多对多映射,在EF中有两种方法:

1)两个ICollection属性(您已经在使用):

public class Instructor
{
    public Int32 Id { get; set; }
    public ICollection<Course> Courses { get; set; }
}
public class Course
{
    public Int32 Id { get; set; }
    public ICollection<Instructor> Instructors { get; set; }
}

在这种情况下,EF将生成/使用映射表,例如:

CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[Instructor_Courses_Mapping]([Id],[InstructorId],[CoursesId])

现在,正如您所指出的那样,无法从该映射表中获取列表,使您可以使用导航属性将集合加载到内存中。所以:

2)但是,您可以使用自己的自定义映射实体覆盖EF的映射表生成:

public class Instructor
{
    public Int32 Id { get; set; }
    public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class Course
{
    public Int32 Id { get; set; }
    public ICollection<InstructorCourse> InstructorCourses { get; set; }
}
public class InstructorCourse
{
    public Int32 Id { get; set; }

    public Int32 InstructorId { get; set; }
    public Instructor Instructor { get; set; }

    public Int32 CourseId { get; set; }
    public Course Course { get; set; }
}

现在,EF将生成这些表:

CREATE TABLE [dbo].[Instructors]([Id])
CREATE TABLE [dbo].[Courses]([Id])
CREATE TABLE [dbp].[InstructorCourses]([Id],[InstructorId],[CourseId])

这将允许您使用dbContext查询InstructorCourses

var instructorCourses = dbContext.Set<InstructorCourse>().Where( c => c.InstructorId == instructorId ).ToList()

这将返回InstructorCourse个对象的列表,其中所有InstructorId值与您要查找的值匹配。然后,如果您想添加/删除映射:

//add item
dbContext.Set<InstructorCourse>().Add(new InstructorCourse() 
{ 
    InstructorId = instructorId, 
    CourseId = courseId 
});
dbContext.SaveChanges();
//remove item
var itemToRemove = dbContext.Set<InstructorCourse>().FirstOrDefault( c => c.InstructorId == instructorId && c.CourseId == courseId);
dbContext.Set<InstructorCourse>().Remove(itemToRemove);
dbContext.SaveChanges();

我发现这个方法更清晰,表示你的数据库结构更清晰,但它确实使嵌套的Linq语句更复杂,并且nulls(没有适当的外键限制)可能更常见。