ASP.NET MVC 애플리케이션에서 Entity Framework를 사용하여 관련 데이터 업데이트(10개 중 6개)

작성자 : Tom Dykstra

Contoso University 샘플 웹 애플리케이션은 Entity Framework 5 Code First 및 Visual Studio 2012를 사용하여 ASP.NET MVC 4 애플리케이션을 만드는 방법을 보여 줍니다. 자습서 시리즈에 대한 정보는 시리즈의 첫 번째 자습서를 참조하세요.

참고

resolve 수 없는 문제가 발생하면 완료된 장을 다운로드하고 문제를 재현해 보세요. 일반적으로 코드를 완료된 코드와 비교하여 문제에 대한 해결 방법을 찾을 수 있습니다. 몇 가지 일반적인 오류 및 해결 방법은 오류 및 해결 방법을 참조하세요.

이전 자습서에서는 관련 데이터를 표시했습니다. 이 자습서에서는 관련 데이터를 업데이트합니다. 대부분의 관계에서는 적절한 외래 키 필드를 업데이트하여 이 작업을 수행할 수 있습니다. 다 대 다 관계의 경우 Entity Framework는 조인 테이블을 직접 노출하지 않으므로 적절한 탐색 속성에서 엔터티를 명시적으로 추가 및 제거해야 합니다.

다음 그림에서는 사용할 페이지를 보여 줍니다.

과정 만들기 페이지를 보여 주는 스크린샷

강사 편집 페이지를 표시하는 스크린샷.

강좌에 대한 만들기 및 편집 페이지 사용자 지정

새 강좌 엔터티가 만들어질 때 기존 부서에 대한 관계가 있어야 합니다. 이를 수행하기 위해 스캐폴드 코드는 컨트롤러 메서드 및 부서를 선택하기 위한 드롭다운 목록을 포함하는 만들기 및 편집 보기를 포함합니다. 드롭다운 목록은 Course.DepartmentID 외래 키 속성을 설정하고, 이는 적절한 Department 엔터티로 Department 탐색 속성을 로드하기 위해 필요한 모든 Entity Framework입니다. 스캐폴드 코드를 사용하지만 오류 처리를 추가하고 드롭다운 목록을 정렬하도록 약간 변경합니다.

CourseController.cs에서 네 Edit 가지 및 Create 메서드를 삭제하고 다음 코드로 바꿉니다.

public ActionResult Create()
{
   PopulateDepartmentsDropDownList();
   return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
   [Bind(Include = "CourseID,Title,Credits,DepartmentID")]
   Course course)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Courses.Add(course);
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
      ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
   }
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

public ActionResult Edit(int id)
{
   Course course = db.Courses.Find(id);
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
    [Bind(Include = "CourseID,Title,Credits,DepartmentID")]
    Course course)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Entry(course).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
      ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
   }
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
   var departmentsQuery = from d in db.Departments
                          orderby d.Name
                          select d;
   ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment);
} 

메서드는 PopulateDepartmentsDropDownList 이름별로 정렬된 모든 부서의 목록을 가져오고, 드롭다운 목록에 대한 컬렉션을 만들고 SelectList , 컬렉션을 속성의 뷰에 ViewBag 전달합니다. 메서드는 호출 코드가 드롭다운 목록이 렌더링될 때 선택될 항목을 지정하도록 허용하는 선택적 selectedDepartment 매개 변수를 허용합니다. 뷰는 도우미에 DropDownList 이름을DepartmentID 전달하고 도우미는 명명DepartmentID된 에 대한 SelectList 개체를 ViewBag 확인합니다.

메서드는 HttpGetCreate 선택한 항목을 설정하지 않고 메서드를 호출 PopulateDepartmentsDropDownList 합니다. 새 과정의 경우 부서가 아직 설정되지 않았기 때문입니다.

public ActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}

메서드는 HttpGetEdit 편집 중인 과정에 이미 할당된 부서의 ID에 따라 선택한 항목을 설정합니다.

public ActionResult Edit(int id)
{
    Course course = db.Courses.Find(id);
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

EditCreate 대한 메서드에는 HttpPost 오류 발생 후 페이지를 다시 표시할 때 선택한 항목을 설정하는 코드도 포함됩니다.

catch (DataException /* dex */)
{
    //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
    ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);

이 코드는 페이지가 오류 메시지를 표시하기 위해 다시 표시될 때 선택된 부서가 선택된 상태로 유지되도록 합니다.

Views\Course\Create.cshtml에서 강조 표시된 코드를 추가하여 제목 필드 앞에 새 과정 번호 필드를 만듭니다. 이전 자습서에서 설명한 대로 기본 키 필드는 기본적으로 스캐폴드되지 않지만 이 기본 키는 의미가 있으므로 사용자가 키 값을 입력할 수 있도록 합니다.

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Course</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.CourseID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.CourseID)
            @Html.ValidationMessageFor(model => model.CourseID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Credits)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Credits)
            @Html.ValidationMessageFor(model => model.Credits)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.DepartmentID, "Department")
        </div>
        <div class="editor-field">
            @Html.DropDownList("DepartmentID", String.Empty)
            @Html.ValidationMessageFor(model => model.DepartmentID)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Views\Course\Edit.cshtml, Views\Course\Delete.cshtmlViews\Course\Details.cshtml에서 제목 필드 앞에 과정 번호 필드를 추가합니다. 기본 키이므로 표시되지만 변경할 수는 없습니다.

<div class="editor-label">
    @Html.LabelFor(model => model.CourseID)
</div>
<div class="editor-field">
    @Html.DisplayFor(model => model.CourseID)
</div>

만들기 페이지를 실행하고(과정 인덱스 페이지를 표시하고 새로 만들기를 클릭) 새 과정에 대한 데이터를 입력합니다.

Course_create_page

만들기를 클릭합니다. 과정 인덱스 페이지가 목록에 추가된 새 과정과 함께 표시됩니다. 인덱스 페이지 목록의 부서 이름은 관계가 올바르게 설정되었음을 표시하는 탐색 속성에서 제공됩니다.

Course_Index_page_showing_new_course

편집 페이지를 실행합니다(과정 인덱스 페이지를 표시하고 과정에서 편집 클릭).

Course_edit_page

페이지에서 데이터를 변경하고 저장을 클릭합니다. 과정 인덱스 페이지는 업데이트된 과정 데이터와 함께 표시됩니다.

강사용 편집 페이지 추가

강사 레코드를 편집할 때 강사의 사무실 할당을 업데이트할 수 있습니다. Instructor 엔터티는 엔터티와 일대일 관계를 맺 OfficeAssignment 습니다. 즉, 다음과 같은 상황을 처리해야 합니다.

  • 사용자가 사무실 할당을 지우고 원래 값이 있는 경우 엔터티를 OfficeAssignment 제거하고 삭제해야 합니다.
  • 사용자가 사무실 할당 값을 입력하고 원래 비어 있는 경우 새 OfficeAssignment 엔터티를 만들어야 합니다.
  • 사용자가 사무실 할당 값을 변경하는 경우 기존 OfficeAssignment 엔터티의 값을 변경해야 합니다.

InstructorController.cs를 열고 메서드를 확인합니다HttpGetEdit.

public ActionResult Edit(int id = 0)
{
    Instructor instructor = db.Instructors.Find(id);
    if (instructor == null)
    {
        return HttpNotFound();
    }
    ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID);
    return View(instructor);
}

여기서 스캐폴드된 코드는 원하는 코드가 아닙니다. 드롭다운 목록에 대한 데이터를 설정하지만 필요한 것은 텍스트 상자입니다. 이 메서드를 다음 코드로 바꿉다.

public ActionResult Edit(int id)
{
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Where(i => i.InstructorID == id)
        .Single();
    return View(instructor);
}

이 코드는 ViewBag 문을 삭제하고 연결된 OfficeAssignment 엔터티에 대한 즉시 로드를 추가합니다. 메서드를 Find 사용하여 즉시 로드를 수행할 수 없으므로 WhereSingle 메서드를 대신 사용하여 강사를 선택합니다.

메서드를 HttpPostEdit 다음 코드로 바꿉 있습니다. Office 할당 업데이트를 처리하는 다음을 수행합니다.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection)
{
   var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Where(i => i.InstructorID == id)
       .Single();

   if (TryUpdateModel(instructorToUpdate, "",
      new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
   {
      try
      {
         if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
         {
            instructorToUpdate.OfficeAssignment = null;
         }

         db.Entry(instructorToUpdate).State = EntityState.Modified;
         db.SaveChanges();

         return RedirectToAction("Index");
      }
      catch (DataException /* dex */)
      {
         //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
         ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
      }
   }
   ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", id);
   return View(instructorToUpdate);
}

코드는 다음을 수행합니다.

  • OfficeAssignment 탐색 속성에 대한 즉시 로드를 사용하여 데이터베이스에서 현재 Instructor 엔터티를 가져옵니다. 이는 메서드에서 HttpGetEdit 수행된 것과 동일합니다.

  • 모델 바인더의 값으로 검색된 Instructor 엔터티를 업데이트합니다. 사용된 TryUpdateModel 오버로드를 사용하면 포함하려는 속성을 안전하게 목록에 추가할 수 있습니다. 이렇게 하면 두 번째 자습서에 설명된 대로 과도하게 게시할 수 없습니다.

    if (TryUpdateModel(instructorToUpdate, "",
          new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    
  • 사무실 위치가 비어 있는 경우 OfficeAssignment 테이블의 관련된 행이 삭제되도록 Instructor.OfficeAssignment 속성을 Null로 설정합니다.

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • 변경 내용을 데이터베이스에 저장합니다.

Views\Instructor\Edit.cshtml에서 고용 날짜 필드의 div 요소 다음에 사무실 위치를 편집하기 위한 새 필드를 추가합니다.

<div class="editor-label">
    @Html.LabelFor(model => model.OfficeAssignment.Location)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.OfficeAssignment.Location)
    @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>

페이지를 실행합니다(강사 탭 선택한 다음 강사에서 편집 클릭). 사무실 위치를 변경하고 저장을 클릭합니다.

Changing_the_office_location

강사 편집 페이지에 과정 과제 추가

강사는 강좌 수에 관계 없이 가르칠 수 있습니다. 이제 다음 스크린샷에 표시된 것처럼 확인란 그룹을 사용하여 강좌 할당을 변경하는 기능을 추가하여 강사 편집 페이지를 향상시킵니다.

과정과 함께 강사 편집 페이지를 보여 주는 스크린샷.

Instructor 엔터티 간의 Course 관계는 다 대 다입니다. 즉, 조인 테이블에 직접 액세스할 수 없습니다. 대신 탐색 속성에 엔터티를 추가 및 Instructor.Courses 제거합니다.

강사에게 할당된 강좌를 변경할 수 있도록 하는 UI는 확인란의 그룹입니다. 데이터베이스의 모든 강좌에 대한 확인란이 표시되고 강사에게 현재 할당되어 있는 것이 선택됩니다. 사용자는 확인란을 선택하거나 선택 취소하여 강좌 할당을 변경할 수 있습니다. 과정 수가 훨씬 더 많은 경우 보기에서 데이터를 표시하는 다른 방법을 사용하려고 하지만 관계를 만들거나 삭제하기 위해 탐색 속성을 조작하는 동일한 방법을 사용합니다.

확인란의 목록에 대한 보기에 데이터를 제공하려면 보기 모델 클래스를 사용합니다. ViewModels 폴더에 AssignedCourseData.cs를 만들고 기존 코드를 다음 코드로 바꿉니다.

namespace ContosoUniversity.ViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

InstructorController.cs에서 메서드를 HttpGetEdit 다음 코드로 바꿉니다. 변경 내용은 강조 표시되어 있습니다.

public ActionResult Edit(int id)
{
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses)
        .Where(i => i.InstructorID == id)
        .Single();
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

private void PopulateAssignedCourseData(Instructor instructor)
{
    var allCourses = db.Courses;
    var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID));
    var viewModel = new List<AssignedCourseData>();
    foreach (var course in allCourses)
    {
        viewModel.Add(new AssignedCourseData
        {
            CourseID = course.CourseID,
            Title = course.Title,
            Assigned = instructorCourses.Contains(course.CourseID)
        });
    }
    ViewBag.Courses = viewModel;
}

코드는 Courses 탐색 속성에 대해 즉시 로드를 추가하고 새 PopulateAssignedCourseData 메서드를 호출하여 AssignedCourseData 보기 모델 클래스를 사용하여 확인란 배열에 대한 정보를 제공합니다.

PopulateAssignedCourseData 메서드의 코드는 보기 모델 클래스를 사용하는 강좌의 목록을 로드하기 위해 모든 Course 엔터티를 통해 읽습니다. 각 강좌의 경우 코드는 강좌가 강사의 Courses 탐색 속성에 있는지 여부를 확인합니다. 강사에게 강좌가 할당되었는지 여부를 확인할 때 효율적인 조회를 만들기 위해 강사에게 할당된 과정은 HashSet 컬렉션에 배치됩니다. 속성은 Assigned 강사가 true 할당된 과정에 대해 로 설정됩니다. 보기는 이 속성을 사용하여 선택된 것으로 표시되어야 하는 확인란을 결정합니다. 마지막으로 목록이 속성의 뷰에 ViewBag 전달됩니다.

다음으로 사용자가 저장을 클릭할 때 실행되는 코드를 추가합니다. 메서드를 HttpPostEdit 엔터티의 탐색 속성을 업데이트하는 새 메서드를 호출하는 다음 코드로 Instructor 바꿉니다Courses. 변경 내용은 강조 표시되어 있습니다.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses)
{
   var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Include(i => i.Courses)
       .Where(i => i.InstructorID == id)
       .Single();
   if (TryUpdateModel(instructorToUpdate, "", 
      new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
   {
      try
      {
         if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
         {
            instructorToUpdate.OfficeAssignment = null;
         }

         UpdateInstructorCourses(selectedCourses, instructorToUpdate);

         db.Entry(instructorToUpdate).State = EntityState.Modified;
         db.SaveChanges();

         return RedirectToAction("Index");
      }
      catch (DataException /* dex */)
      {
         //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
         ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
      }
   }
   PopulateAssignedCourseData(instructorToUpdate);
   return View(instructorToUpdate);
}

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

뷰에 엔터티 컬렉션 Course 이 없으므로 모델 바인더는 탐색 속성을 자동으로 업데이트할 Courses 수 없습니다. 모델 바인더를 사용하여 Courses 탐색 속성을 업데이트하는 대신 새 UpdateInstructorCourses 메서드에서 업데이트합니다. 따라서 모델 바인딩에서 Courses 속성을 제외해야 합니다. 허용 목록 오버로드 Courses 를 사용하고 있고 포함 목록에 없기 때문에 TryUpdateModel을 호출하는 코드를 변경할 필요가 없습니다.

검사 상자를 선택하지 않은 경우 의 코드는 UpdateInstructorCourses 빈 컬렉션을 사용하여 탐색 속성을 초기화합니다Courses.

if (selectedCourses == null)
{
    instructorToUpdate.Courses = new List<Course>();
    return;
}

그런 다음, 코드는 데이터베이스의 모든 강좌를 반복하고 현재 강사에게 할당된 것과 보기에서 선택되었던 것에 대해 각 강좌를 확인합니다. 효율적인 조회를 수행하기 위해 후자의 두 컬렉션은 HashSet 개체에 저장됩니다.

강좌에 대한 확인란이 선택됐지만 강좌가 Instructor.Courses 탐색 속성에 없는 경우 강좌는 탐색 속성의 컬렉션에 추가됩니다.

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Add(course);
    }
}

강좌에 대한 확인란이 선택되지 않았지만 강좌가 Instructor.Courses 탐색 속성에 있는 경우 강좌는 탐색 속성에서 제거됩니다.

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Remove(course);
    }
}

Views\Instructor\Edit.cshtml에서 필드의 요소 OfficeAssignment 바로 다음에 강조 표시된 다음 코드를 추가하여 검사 상자 배열이 div 있는 Courses 필드를 추가합니다.

@model ContosoUniversity.Models.Instructor

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Instructor</legend>

        @Html.HiddenFor(model => model.InstructorID)

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstMidName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstMidName)
            @Html.ValidationMessageFor(model => model.FirstMidName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.HireDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.HireDate)
            @Html.ValidationMessageFor(model => model.HireDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.OfficeAssignment.Location)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.OfficeAssignment.Location)
            @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
        </div>

        <div class="editor-field">
    <table>
        <tr>
            @{
                int cnt = 0;
                List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;

                foreach (var course in courses) {
                    if (cnt++ % 3 == 0) {
                        @:  </tr> <tr> 
                    }
                    @: <td> 
                        <input type="checkbox" 
                               name="selectedCourses" 
                               value="@course.CourseID" 
                               @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /> 
                        @course.CourseID @:  @course.Title
                    @:</td>
                }
                @: </tr>
            }
    </table>
</div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

이 코드는 세 개의 열이 있는 HTML 테이블을 만듭니다. 각 열은 강좌 번호 및 제목으로 구성된 캡션이 뒤에 오는 확인란입니다. 검사 상자는 모두 동일한 이름("selectedCourses")을 가지며, 이는 모델 바인더가 그룹으로 처리되어야 함을 알립니다. 각 검사 상자의 특성은 valueCourseID. 페이지가 게시될 때 모델 바인더가 선택한 검사 상자의 CourseID 값으로 구성된 배열을 컨트롤러에 전달합니다.

검사 상자가 처음 렌더링되면 강사 checked 에게 할당된 과정에 대한 상자에는 특성을 선택하여 선택합니다(선택된 것으로 표시됨).

과정 과제를 변경한 후에는 사이트가 페이지로 돌아올 Index 때 변경 내용을 확인할 수 있습니다. 따라서 해당 페이지의 테이블에 열을 추가해야 합니다. 이 경우 표시하려는 정보가 모델로 페이지에 전달하는 엔터티의 Instructor 탐색 속성에 Courses 이미 있으므로 개체를 사용할 ViewBag 필요가 없습니다.

Views\Instructor\Index.cshtml에서 다음 예제와 같이 Office 제목 바로 다음에 Courses 제목을 추가합니다.

<tr> 
    <th></th> 
    <th>Last Name</th> 
    <th>First Name</th> 
    <th>Hire Date</th> 
    <th>Office</th>
    <th>Courses</th>
</tr>

그런 다음 사무실 위치 세부 정보 셀 바로 다음에 새 세부 정보 셀을 추가합니다.

@model ContosoUniversity.ViewModels.InstructorIndexData

@{
    ViewBag.Title = "Instructors";
}

<h2>Instructors</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Hire Date</th>
        <th>Office</th>
        <th>Courses</th>
    </tr>
    @foreach (var item in Model.Instructors)
    {
        string selectedRow = "";
        if (item.InstructorID == ViewBag.InstructorID)
        {
            selectedRow = "selectedrow";
        } 
        <tr class="@selectedRow" valign="top">
            <td>
                @Html.ActionLink("Select", "Index", new { id = item.InstructorID }) | 
                @Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) | 
                @Html.ActionLink("Details", "Details", new { id = item.InstructorID }) | 
                @Html.ActionLink("Delete", "Delete", new { id = item.InstructorID })
            </td>
            <td>
                @item.LastName
            </td>
            <td>
                @item.FirstMidName
            </td>
            <td>
                @String.Format("{0:d}", item.HireDate)
            </td>
            <td>
                @if (item.OfficeAssignment != null)
                { 
                    @item.OfficeAssignment.Location  
                }
            </td>
            <td>
                @{
                foreach (var course in item.Courses)
                {
                    @course.CourseID @:  @course.Title <br />
                }
                }
            </td>
        </tr> 
    }
</table>

@if (Model.Courses != null)
{ 
    <h3>Courses Taught by Selected Instructor</h3> 
    <table>
        <tr>
            <th></th>
            <th>ID</th>
            <th>Title</th>
            <th>Department</th>
        </tr>

        @foreach (var item in Model.Courses)
        {
            string selectedRow = "";
            if (item.CourseID == ViewBag.CourseID)
            {
                selectedRow = "selectedrow";
            } 
        
            <tr class="@selectedRow">

                <td>
                    @Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
                </td>
                <td>
                    @item.CourseID
                </td>
                <td>
                    @item.Title
                </td>
                <td>
                    @item.Department.Name
                </td>
            </tr> 
        }

    </table> 
}
@if (Model.Enrollments != null)
{ 
    <h3>Students Enrolled in Selected Course</h3> 
    <table>
        <tr>
            <th>Name</th>
            <th>Grade</th>
        </tr>
        @foreach (var item in Model.Enrollments)
        { 
            <tr>
                <td>
                    @item.Student.FullName
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Grade)
                </td>
            </tr> 
        }
    </table> 
}

강사 인덱스 페이지를 실행하여 각 강사에게 할당된 과정을 확인합니다.

Instructor_index_page

강사에서 편집 을 클릭하여 편집 페이지를 확인합니다.

Instructor_edit_page_with_courses

일부 과정 과제를 변경하고 저장을 클릭합니다. 변경 내용은 인덱스 페이지에 반영됩니다.

참고: 강사 과정 데이터를 편집하는 방법은 제한된 수의 과정이 있을 때 잘 작동합니다. 훨씬 큰 컬렉션의 경우 다른 UI 및 다른 업데이트 메서드가 필요합니다.

Delete 메서드 업데이트

강사가 삭제되면 사무실 할당 레코드(있는 경우)가 삭제되도록 HttpPost Delete 메서드의 코드를 변경합니다.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
   Instructor instructor = db.Instructors
     .Include(i => i.OfficeAssignment)
     .Where(i => i.InstructorID == id)
     .Single();

   instructor.OfficeAssignment = null;
   db.Instructors.Remove(instructor);
   db.SaveChanges();
   return RedirectToAction("Index");
}

관리자 권한으로 부서에 할당된 강사를 삭제하려고 하면 참조 무결성 오류가 발생합니다. 강사가 관리자로 할당된 모든 부서에서 강사를 자동으로 제거하는 추가 코드는 이 자습서의 현재 버전을 참조하세요.

요약

이제 관련 데이터 작업에 대한 이 소개를 완료했습니다. 지금까지 이러한 자습서에서는 전체 범위의 CRUD 작업을 수행했지만 동시성 문제는 처리하지 않았습니다. 다음 자습서에서는 동시성 항목을 소개하고, 처리 옵션을 설명하고, 한 엔터티 형식에 대해 이미 작성한 CRUD 코드에 동시성 처리를 추가합니다.

다른 Entity Framework 리소스에 대한 링크는 이 시리즈의 마지막 자습서 끝에 있습니다.