다음을 통해 공유


ASP.NET MVC 애플리케이션에서 Entity Framework를 사용하여 관련 데이터 읽기(5/10)

작성자: Tom Dykstra

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

참고

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

이전 자습서에서는 School 데이터 모델을 완료했습니다. 이 자습서에서는 관련 데이터, 즉 Entity Framework가 탐색 속성에 로드하는 데이터를 읽고 표시합니다.

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

Contoso University 과정 인덱스 페이지를 보여 주는 스크린샷

강사와 해당 과정 중 하나가 선택된 Contoso University 강사 인덱스 페이지를 보여 주는 스크린샷

Entity Framework는 엔터티의 탐색 속성에 관련 데이터를 로드할 수 있는 여러 가지 방법이 있습니다.

  • 지연 로드. 엔터티를 처음 읽을 때 관련된 데이터가 검색되지 않습니다. 그러나 탐색 속성에 처음으로 액세스하려고 할 때 해당 탐색 속성에 필요한 데이터가 자동으로 검색됩니다. 이렇게 하면 엔터티 자체에 대한 쿼리와 엔터티에 대한 관련 데이터를 검색해야 할 때마다 하나씩 여러 쿼리가 데이터베이스로 전송됩니다.

    Lazy_loading_example

  • 즉시 로드. 엔터티를 읽을 때 관련된 데이터가 함께 검색됩니다. 이는 일반적으로 필요한 데이터를 모두 검색하는 단일 조인 쿼리를 발생시킵니다. 메서드를 사용하여 즉시 로드를 지정합니다 Include .

    Eager_loading_example

  • 명시적 로드. 코드에서 관련 데이터를 명시적으로 검색한다는 점을 제외하고 지연 로드와 비슷합니다. 탐색 속성에 액세스할 때 자동으로 발생하지 않습니다. 엔터티에 대한 개체 상태 관리자 항목을 가져오고 컬렉션에 대한 메서드 또는 Reference.Load 단일 엔터티를 보유하는 속성에 대한 메서드를 호출 Collection.Load 하여 관련 데이터를 수동으로 로드합니다. (다음 예제에서는 관리자 탐색 속성을 로드하려는 경우 을 Reference(x => x.Administrator)로 바꿉 Collection(x => x.Courses) 니다.)

    Explicit_loading_example

속성 값을 즉시 검색하지 않으므로 지연 로드 및 명시적 로드를 모두 지연 로드라고도 합니다.

일반적으로 검색된 모든 엔터티에 대한 관련 데이터가 필요하다는 것을 알고 있는 경우 데이터베이스로 전송되는 단일 쿼리가 검색된 각 엔터티에 대한 별도의 쿼리보다 일반적으로 더 효율적이므로 즉시 로드하면 최상의 성능을 제공합니다. 예를 들어 위의 예제에서 각 부서에 10개의 관련 과정이 있다고 가정합니다. 즉시 로드 예제를 사용하면 단일(조인) 쿼리와 데이터베이스로의 단일 왕복만 발생합니다. 지연 로드 및 명시적 로드 예제는 모두 11개의 쿼리와 11번의 데이터베이스 왕복을 초래합니다. 데이터베이스에 대한 추가 왕복은 대기 시간이 길 때 성능에 특히 악영향을 줍니다.

반면에 일부 시나리오에서는 지연 로드가 더 효율적입니다. 즉시 로드하면 매우 복잡한 조인이 생성되어 SQL Server 효율적으로 처리할 수 없습니다. 또는 처리 중인 엔터티 집합의 하위 집합에 대해서만 엔터티의 탐색 속성에 액세스해야 하는 경우 지연 로드는 필요한 것보다 더 많은 데이터를 검색하기 때문에 지연 로드가 더 잘 수행될 수 있습니다. 성능이 중요한 경우 최상의 선택을 위해 두 가지 방식으로 성능을 테스트하는 것이 가장 좋습니다.

일반적으로 지연 로드를 해제한 경우에만 명시적 로드를 사용합니다. 지연 로드를 해제해야 하는 한 가지 시나리오는 serialization 중입니다. 지연 로드 및 serialization은 잘 섞이지 않으며, 주의하지 않으면 지연 로드를 사용하도록 설정할 때 의도한 것보다 훨씬 많은 데이터를 쿼리할 수 있습니다. 직렬화는 일반적으로 형식의 instance 각 속성에 액세스하여 작동합니다. 속성 액세스는 지연 로드를 트리거하고 지연 로드된 엔터티는 직렬화됩니다. 그런 다음 serialization 프로세스는 지연 로드된 엔터티의 각 속성에 액세스하여 더 지연 로드 및 serialization을 유발할 수 있습니다. 이 런어웨이 체인 반응을 방지하려면 엔터티를 직렬화하기 전에 지연 로드를 끕니다.

데이터베이스 컨텍스트 클래스는 기본적으로 지연 로드를 수행합니다. 지연 로드를 사용하지 않도록 설정하는 방법에는 두 가지가 있습니다.

  • 특정 탐색 속성의 경우 속성을 선언할 virtual 때 키워드(keyword) 생략합니다.

  • 모든 탐색 속성에 대해 를 로 false설정합니다LazyLoadingEnabled. 예를 들어 컨텍스트 클래스의 생성자에 다음 코드를 넣을 수 있습니다.

    this.Configuration.LazyLoadingEnabled = false;
    

지연 로드는 성능 문제를 일으키는 코드를 마스킹할 수 있습니다. 예를 들어 열성 또는 명시적 로드를 지정하지 않지만 대량의 엔터티를 처리하고 각 반복에서 여러 탐색 속성을 사용하는 코드는 매우 비효율적일 수 있습니다(데이터베이스로의 많은 왕복으로 인해). 온-프레미스 SQL 서버를 사용하여 개발에서 잘 작동하는 애플리케이션은 대기 시간 증가 및 지연 로드로 인해 Azure SQL Database로 이동할 때 성능 문제가 있을 수 있습니다. 실제 테스트 로드를 사용하여 데이터베이스 쿼리를 프로파일링하면 지연 로드가 적절한지 확인하는 데 도움이 됩니다. 자세한 내용은 Entity Framework 전략 무시: 관련 데이터 로드Entity Framework를 사용하여 네트워크 대기 시간을 줄여 SQL Azure.

부서 이름을 표시하는 과정 인덱스 페이지 만들기

Course 엔터티는 강좌에 할당된 부서의 Department 엔터티를 포함하는 탐색 속성을 포함합니다. 과정 목록에 할당된 부서의 이름을 표시하려면 탐색 속성에 있는 Course.Department 엔터티에서 Department 속성을 가져와 Name 야 합니다.

다음 그림과 같이 이전에 컨트롤러에 대해 했던 Student 것과 동일한 옵션을 사용하여 엔터티 형식에 대한 Course 라는 CourseController 컨트롤러를 만듭니다(이미지와 달리 컨텍스트 클래스는 Models 네임스페이스가 아닌 DAL 네임스페이스에 있음).

Add_Controller_dialog_box_for_Course_controller

Controllers\CourseController.cs를 열고 메서드를 Index 확인합니다.

public ViewResult Index()
{
    var courses = db.Courses.Include(c => c.Department);
    return View(courses.ToList());
}

자동 스캐폴딩은 Include 메서드를 사용하여 Department 탐색 속성에 대해 즉시 로드를 지정했습니다.

Views\Course\Index.cshtml을 열고 기존 코드를 다음 코드로 바꿉니다. 변경 내용이 강조 표시되어 있습니다.

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewBag.Title = "Courses";
}

<h2>Courses</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Number</th>
        <th>Title</th>
        <th>Credits</th>
        <th>Department</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
            @Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.CourseID)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Title)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Credits)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Department.Name)
        </td>
    </tr>
}
</table>

스캐폴드 코드에 다음 변경 내용을 만들었습니다.

  • 제목을 Index에서 Courses로 변경했습니다.
  • 행 링크를 왼쪽으로 이동했습니다.
  • 속성 값을 보여 주는 열이 제목 번호 아래에 추가되었습니다 CourseID . 기본 키는 일반적으로 최종 사용자에게 의미가 없으므로 기본적으로 스캐폴드되지 않습니다. 그러나 이 경우 기본 키는 의미가 있으며 표시하려고 합니다.)
  • 마지막 열 머리글을 DepartmentID (외래 키 Department 의 이름에서 엔터티로)에서 Department로 변경했습니다.

마지막 열의 경우 스캐폴드된 코드는 탐색 속성에 DepartmentDepartment 로드된 엔터티의 속성을 표시 Name 합니다.

<td>
    @Html.DisplayFor(modelItem => item.Department.Name)
</td>

페이지를 실행(Contoso University 홈페이지에서 과정 탭 선택)하여 부서 이름이 있는 목록을 확인합니다.

Courses_index_page_with_department_names

과정 및 등록을 표시하는 강사 인덱스 페이지 만들기

이 섹션에서는 강사 인덱스 페이지를 표시하기 위해 엔터티에 대한 Instructor 컨트롤러와 뷰를 만듭니다.

강사와 해당 과정 중 하나가 선택된 강사 인덱스 페이지를 보여 주는 스크린샷

이 페이지는 다음과 같은 방법으로 관련된 데이터를 읽고 표시합니다.

  • 강사 목록은 OfficeAssignment 엔터티에서 관련된 데이터를 표시합니다. InstructorOfficeAssignment 엔터티는 일대영 또는 일 관계에 있습니다. OfficeAssignment 엔터티에 대해 즉시 로드를 사용합니다. 이전에 설명한 대로 기본 테이블의 검색된 모든 행에 관련된 데이터가 필요한 경우 즉시 로드는 일반적으로 더 효율적입니다. 이 경우 표시된 모든 강사에 대한 사무실 할당을 표시하길 원합니다.
  • 사용자가 강사를 선택하면 관련된 Course 엔터티가 표시됩니다. InstructorCourse 엔터티는 다대다 관계에 있습니다. Course 엔터티 및 관련된 Department 엔터티에 대해 즉시 로드를 사용합니다. 이 경우 선택한 강사만 강좌가 필요하므로 지연 로드가 더 효율적일 수 있습니다. 그러나 이 예제에서는 탐색 속성에 있는 엔터티 내에서 탐색 속성에 대한 즉시 로드를 사용하는 방법을 보여 줍니다.
  • 사용자가 과정을 선택하면 Enrollments 엔터티 집합의 관련 데이터가 표시됩니다. CourseEnrollment 엔터티는 일대다 관계에 있습니다. 엔터티 및 해당 관련 Student 엔터티에 대한 Enrollment 명시적 로드를 추가합니다. (지연 로드를 사용하도록 설정했기 때문에 명시적 로드는 필요하지 않지만 명시적 로드를 수행하는 방법을 보여 줍니다.)

강사 인덱스 보기에 대한 보기 모델 만들기

강사 인덱스 페이지에는 세 개의 다른 테이블이 표시됩니다. 따라서 각각이 테이블 중 하나에 대한 데이터를 보유하는 세 가지 속성을 포함하는 보기 모델을 만듭니다.

ViewModels 폴더에서 InstructorIndexData.cs를 만들고 기존 코드를 다음 코드로 바꿉니다.

using System.Collections.Generic;
using ContosoUniversity.Models;

namespace ContosoUniversity.ViewModels
{
    public class InstructorIndexData
    {
        public IEnumerable<Instructor> Instructors { get; set; }
        public IEnumerable<Course> Courses { get; set; }
        public IEnumerable<Enrollment> Enrollments { get; set; }
    }
}

선택한 행에 대한 스타일 추가

선택한 행을 표시하려면 다른 배경색이 필요합니다. 이 UI에 대한 스타일을 제공하려면 아래와 같이 Content\Site.css의 섹션 /* info and errors */ 에 강조 표시된 다음 코드를 추가합니다.

/* info and errors */
.selectedrow 
{ 
    background-color: #a4d4e6; 
}
.message-info {
    border: 1px solid;
    clear: both;
    padding: 10px 20px;
}

강사 컨트롤러 및 뷰 만들기

다음 그림과 InstructorController 같이 컨트롤러를 만듭니다.

Add_Controller_dialog_box_for_Instructor_controller

Controllers\InstructorController.cs를using 열고 네임스페이 ViewModels 스에 대한 문을 추가합니다.

using ContosoUniversity.ViewModels;

메서드의 Index 스캐폴드된 코드는 탐색 속성에 대해서만 OfficeAssignment 즉시 로드를 지정합니다.

public ViewResult Index()
{
    var instructors = db.Instructors.Include(i => i.OfficeAssignment);
    return View(instructors.ToList());
}

메서드를 Index 다음 코드로 바꿔 추가 관련 데이터를 로드하고 보기 모델에 넣습니다.

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();
    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.InstructorID == id.Value).Single().Courses;
    }

    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        viewModel.Enrollments = viewModel.Courses.Where(
            x => x.CourseID == courseID).Single().Enrollments;
    }

    return View(viewModel);
}

메서드는 선택한 강사 및 선택한 과정의 ID 값을 제공하고 필요한 모든 데이터를 뷰에 전달하는 선택적 경로 데이터(id) 및 쿼리 문자열 매개 변수(courseID)를 허용합니다. 매개 변수는 페이지의 선택 하이퍼링크에서 제공됩니다.

경로 데이터

경로 데이터는 모델 바인더가 라우팅 테이블에 지정된 URL 세그먼트에서 찾은 데이터입니다. 예를 들어 기본 경로는 , actionid 세그먼트를 지정controller합니다.

routes.MapRoute(  
 name: "Default",  
 url: "{controller}/{action}/{id}",  
 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }  
);

다음 URL에서 기본 경로는 로 actionIndex 매핑 Instructorcontroller되고, 1id은 로 매핑됩니다. 이러한 경로는 경로 데이터 값입니다.

http://localhost:1230/Instructor/Index/1?courseID=2021

"?courseID=2021"은 쿼리 문자열 값입니다. 를 쿼리 문자열 값으로 전달하는 id 경우에도 모델 바인더가 작동합니다.

http://localhost:1230/Instructor/Index?id=1&CourseID=2021

URL은 Razor 뷰의 문에 의해 ActionLink 만들어집니다. 다음 코드에서 매개 변수는 id 기본 경로와 일치하므로 id 경로 데이터에 추가됩니다.

@Html.ActionLink("Select", "Index", new { id = item.PersonID  })

다음 코드에서 는 courseID 기본 경로의 매개 변수와 일치하지 않으므로 쿼리 문자열로 추가됩니다.

@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })

코드는 보기 모델의 인스턴스를 만들고 강사 목록에 배치하여 시작합니다. 코드는 및 탐색 속성에 대한 Instructor.OfficeAssignment 즉시 로드를 Instructor.Courses 지정합니다.

var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
    .Include(i => i.OfficeAssignment)
    .Include(i => i.Courses.Select(c => c.Department))
     .OrderBy(i => i.LastName);

두 번째 Include 메서드는 Courses를 로드하고 로드되는 각 과정에 대해 탐색 속성에 대한 즉시 로드를 Course.Department 수행합니다.

.Include(i => i.Courses.Select(c => c.Department))

앞에서 설명한 것처럼 즉시 로드할 필요는 없지만 성능을 향상시키기 위해 수행됩니다. 보기는 항상 OfficeAssignment 엔터티가 필요하므로 동일한 쿼리에서 페치하는 것이 더 효율적입니다. Course 웹 페이지에서 강사를 선택할 때 엔터티가 필요하므로 빈도 로드는 페이지가 없는 것보다 선택한 과정으로 더 자주 표시되는 경우에만 지연 로드보다 낫습니다.

강사 ID를 선택한 경우 선택한 강사는 보기 모델의 강사 목록에서 검색됩니다. 뷰 모델의 Courses 속성은 해당 강사의 Courses 탐색 속성에서 Course 엔터티로 로드됩니다.

if (id != null)
{
    ViewBag.InstructorID = id.Value;
    viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses;
}

메서드는 Where 컬렉션을 반환하지만, 이 경우 해당 메서드에 전달된 조건에 따라 단일 Instructor 엔터티만 반환됩니다. Single 메서드는 컬렉션을 단일 Instructor 엔터티로 변환합니다. 이는 해당 엔터티의 Courses 속성에 대한 액세스를 제공합니다.

컬렉션에 항목이 하나만 있다는 것을 알고 있는 경우 컬렉션에서 Single 메서드를 사용합니다. 메서드는 Single 전달된 컬렉션이 비어 있거나 둘 이상의 항목이 있는 경우 예외를 throw합니다. 또는 컬렉션이 비어 있는 경우 기본값(null이 경우)을 반환하는 SingleOrDefault가 있습니다. 그러나 이 경우 여전히 예외가 발생하며(참조에서 속성을 null 찾으 Courses 려고 시도함) 예외 메시지는 문제의 원인을 덜 명확하게 나타냅니다. 메서드를 호출할 때 메서드를 Single 별도로 호출 Where 하는 대신 조건을 전달할 Where 수도 있습니다.

.Single(i => i.InstructorID == id.Value)

위 코드를 아래 코드 대신 사용합니다.

.Where(I => i.InstructorID == id.Value).Single()

다음으로 강좌를 선택한 경우 선택한 강좌가 보기 모델의 강좌 목록에서 검색됩니다. 그런 다음 보기 모델의 Enrollments 속성이 해당 과정의 Enrollments 탐색 속성에 Enrollment 있는 엔터티와 함께 로드됩니다.

if (courseID != null)
{
    ViewBag.CourseID = courseID.Value;
    viewModel.Enrollments = viewModel.Courses.Where(
        x => x.CourseID == courseID).Single().Enrollments;
}

강사 인덱스 보기 수정

Views\Instructor\Index.cshtml에서 기존 코드를 다음 코드로 바꿉니다. 변경 내용은 강조 표시되어 있습니다.

@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>
    </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> 
                @Html.DisplayFor(modelItem => item.HireDate)
            </td> 
            <td> 
                @if (item.OfficeAssignment != null) 
                { 
                    @item.OfficeAssignment.Location  
                } 
            </td> 
        </tr> 
    } 
</table>

기존 코드에 다음 변경 내용을 만들었습니다.

  • 모델 클래스를 InstructorIndexData로 변경했습니다.

  • 페이지 제목을 인덱스에서 강사로 변경했습니다.

  • 행 링크 열을 왼쪽으로 이동했습니다.

  • FullName 열을 제거했습니다.

  • 가 null이 아닌 경우에만 item.OfficeAssignment 표시되는 item.OfficeAssignment.LocationOffice 열이 추가되었습니다. (1 대 0 또는 1 관계이므로 관련 OfficeAssignment 엔터티가 없을 수 있습니다.)

    <td> 
        @if (item.OfficeAssignment != null) 
        { 
            @item.OfficeAssignment.Location  
        } 
    </td>
    
  • 선택한 강사의 요소에 tr 동적으로 추가할 class="selectedrow" 코드가 추가되었습니다. 앞에서 만든 CSS 클래스를 사용하여 선택한 행의 배경색을 설정합니다. (특성은 valign 테이블에 여러 행 열을 추가할 때 다음 자습서에서 유용합니다.)

    string selectedRow = ""; 
    if (item.InstructorID == ViewBag.InstructorID) 
    { 
        selectedRow = "selectedrow"; 
    } 
    <tr class="@selectedRow" valign="top">
    
  • 각 행의 다른 링크 바로 앞에 Select라는 레이블이 새로 ActionLink 추가되어 선택한 강사 ID가 메서드로 Index 전송됩니다.

애플리케이션을 실행하고 강사 탭을 선택합니다. 페이지에는 관련 엔터티가 Location 없는 OfficeAssignment 경우 관련 OfficeAssignment 엔터티 및 빈 테이블 셀의 속성이 표시됩니다.

Instructors_index_page_with_nothing_selected

Views\Instructor\Index.cshtml 파일의 닫는 table 요소(파일 끝에 있음) 다음에 강조 표시된 다음 코드를 추가합니다. 그러면 강사를 선택할 때 강사와 관련된 과정 목록이 표시됩니다.

<td> 
                @if (item.OfficeAssignment != null) 
                { 
                    @item.OfficeAssignment.Location  
                } 
            </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> 
}

이 코드는 보기 모델의 Courses 속성을 읽어 강좌의 목록을 표시합니다. 또한 선택한 과정의 ID를 작업 메서드로 보내는 하이퍼링크를 Index 제공합니다Select.

참고

.css 파일은 브라우저에서 캐시됩니다. 애플리케이션을 실행할 때 변경 내용이 표시되지 않으면 하드 새로 고침을 수행합니다( 새로 고침 단추를 클릭하는 동안 Ctrl 키를 누르거나 Ctrl+F5 누르기).

페이지를 실행하고 강사를 선택합니다. 이제 선택된 강사에 할당된 강좌를 표시하는 표가 표시되고 각 강좌에 대해 할당된 부서의 이름이 표시됩니다.

Instructors_index_page_with_instructor_selected

방금 추가한 코드 블록 뒤에 다음 코드를 추가합니다. 해당 강좌가 선택된 경우에 강좌에 등록된 학생의 목록을 표시합니다.

@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> 
}

이 코드는 강좌에 등록한 학생의 목록을 표시하기 위해 보기 모델의 Enrollments 속성을 읽습니다.

페이지를 실행하고 강사를 선택합니다. 그런 다음, 강좌를 선택하여 등록된 학생 및 해당 등급의 목록을 봅니다.

강사와 해당 과정 중 하나가 선택된 강사 인덱스 페이지의 스크린샷.

명시적 로드 추가

InstructorController.cs를 열고 메서드가 Index 선택한 과정의 등록 목록을 가져오는 방법을 살펴봅니다.

if (courseID != null)
{
    ViewBag.CourseID = courseID.Value;
    viewModel.Enrollments = viewModel.Courses.Where(
        x => x.CourseID == courseID).Single().Enrollments;
}

강사 목록을 검색할 때 탐색 속성 및 각 과정의 속성에 대한 Courses 즉시 로드를 Department 지정했습니다. 그런 다음 뷰 모델에 컬렉션을 배치 Courses 하고 이제 해당 컬렉션의 한 엔터티에서 탐색 속성에 액세스합니다 Enrollments . 탐색 속성에 대한 Course.Enrollments 즉시 로드를 지정하지 않았기 때문에 지연 로드의 결과로 해당 속성의 데이터가 페이지에 표시됩니다.

다른 방법으로 코드를 변경하지 않고 지연 로드를 사용하지 않도록 설정한 경우 속성은 Enrollments 코스의 실제로 등록 수에 관계없이 null이 됩니다. 이 경우 속성을 로드 Enrollments 하려면 즉시 로드 또는 명시적 로드를 지정해야 합니다. 이미 즉시 로드하는 방법을 살펴보았습니다. 명시적 로드 예제를 보려면 메서드를 Index 속성을 명시적으로 로드하는 다음 코드로 바꿉니다 Enrollments . 변경된 코드가 강조 표시됩니다.

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();

    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.InstructorID == id.Value).Single().Courses;
    }
    
    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
        db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
        foreach (Enrollment enrollment in selectedCourse.Enrollments)
        {
            db.Entry(enrollment).Reference(x => x.Student).Load();
        }

        viewModel.Enrollments = selectedCourse.Enrollments;
    }

    return View(viewModel);
}

선택한 Course 엔터티를 가져오면 새 코드는 해당 과정의 Enrollments 탐색 속성을 명시적으로 로드합니다.

db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();

그런 다음 각 Enrollment 엔터티의 관련 Student 엔터티를 명시적으로 로드합니다.

db.Entry(enrollment).Reference(x => x.Student).Load();

메서드를 Collection 사용하여 컬렉션 속성을 로드하지만 엔터티가 하나만 있는 속성의 경우 메서드를 Reference 사용합니다. 이제 강사 인덱스 페이지를 실행할 수 있으며 데이터가 검색되는 방법을 변경했지만 페이지에 표시되는 내용에는 차이가 없습니다.

요약

이제 세 가지 방법(지연, 열망 및 명시적)을 모두 사용하여 관련 데이터를 탐색 속성에 로드했습니다. 다음 자습서에서는 관련된 데이터를 업데이트하는 방법을 설명합니다.

다른 Entity Framework 리소스에 대한 링크는 ASP.NET 데이터 액세스 콘텐츠 맵에서 찾을 수 있습니다.