Reading Related Data With The Entity Framework in An ASP - NET MVC Application (5 of 10) - The ASP - NET Site PDF
Reading Related Data With The Entity Framework in An ASP - NET MVC Application (5 of 10) - The ASP - NET Site PDF
The Contoso University sample web application demonstrates how to create ASP.NET MVC 4 applications using the
Entity Framework 5 Code First and Visual Studio 2012. For information about the tutorial series, see the first tutorial
in the series (/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/creating-an-entity-framework-data-model-for-an-
asp-net-mvc-application) . You can start the tutorial series from the beginning or download a starter project for
this chapter (/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/building-the-ef5-mvc4-chapter-downloads) and
start here.
If you run into a problem you can’t resolve, download the completed chapter (/mvc/tutorials
/getting-started-with-ef-5-using-mvc-4/building-the-ef5-mvc4-chapter-downloads) and try to
reproduce your problem. You can generally find the solution to the problem by comparing your
code to the completed code. For some common errors and how to solve them, see Errors and
Workarounds. (/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/advanced-entity-
framework-scenarios-for-an-mvc-web-application#errors)
In the previous tutorial you completed the School data model. In this tutorial you'll read and display related data — that
is, data that the Entity Framework loads into navigation properties.
The following illustrations show the pages that you'll work with.
Lazy loading. When the entity is first read, related data isn't retrieved. However, the first time you attempt to access a
navigation property, the data required for that navigation property is automatically retrieved. This results in multiple
queries sent to the database — one for the entity itself and one each time that related data for the entity must be
retrieved.
Explicit loading. This is similar to lazy loading, except that you explicitly retrieve the related data in code; it doesn't
happen automatically when you access a navigation property. You load related data manually by getting the object
state manager entry for an entity and calling the Collection.Load (http://msdn.microsoft.com/en-us/library
/gg696220(v=vs.103).aspx) method for collections or the Reference.Load (http://msdn.microsoft.com/en-us
/library/gg679166(v=vs.103).aspx) method for properties that hold a single entity. (In the following example, if
you wanted to load the Administrator navigation property, you'd replace Collection(x => x.Courses) with
Reference(x => x.Administrator).)
Because they don't immediately retrieve the property values, lazy loading and explicit loading are also both known as
deferred loading.
In general, if you know you need related data for every entity retrieved, eager loading offers the best performance,
because a single query sent to the database is typically more efficient than separate queries for each entity retrieved. For
example, in the above examples, suppose that each department has ten related courses. The eager loading example
would result in just a single (join) query and a single round trip to the database. The lazy loading and explicit loading
examples would both result in eleven queries and eleven round trips to the database. The extra round trips to the
database are especially detrimental to performance when latency is high.
On the other hand, in some scenarios lazy loading is more efficient. Eager loading might cause a very complex join to be
generated, which SQL Server can't process efficiently. Or if you need to access an entity's navigation properties only for a
subset of a set of entities you're processing, lazy loading might perform better because eager loading would retrieve more
data than you need. If performance is critical, it's best to test performance both ways in order to make the best choice.
The database context class performs lazy loading by default. There are two ways to disable lazy loading:
For specific navigation properties, omit the virtual keyword when you declare the property.
For all navigation properties, set LazyLoadingEnabled to false. For example, you can put the following code in the
constructor of your context class:
this.Configuration.LazyLoadingEnabled = false;
Lazy loading can mask code that causes performance problems. For example, code that doesn't specify eager or explicit
loading but processes a high volume of entities and uses several navigation properties in each iteration might be very
inefficient (because of many round trips to the database). An application that performs well in development using an on
premise SQL server might have performance problems when moved to Windows Azure SQL Database due to the
increased latency and lazy loading. Profiling the database queries with a realistic test load will help you determine if lazy
loading is appropriate. For more information see Demystifying Entity Framework Strategies: Loading Related Data
(http://msdn.microsoft.com/en-us/magazine/hh205756.aspx) and Using the Entity Framework to Reduce Network
Latency to SQL Azure (http://msdn.microsoft.com/en-us/magazine/gg309181.aspx) .
Create a controller named CourseController for the Course entity type, using the same options that you did earlier for
the Student controller, as shown in the following illustration (except unlike the image, your context class is in the DAL
namespace, not the Models namespace):
The automatic scaffolding has specified eager loading for the Department navigation property by using the Include
method.
Open Views\Course\Index.cshtml and replace the existing code with the following code. The changes are highlighted:
@model IEnumerable<ContosoUniversity.Models.Course>
@{
ViewBag.Title = "Courses";
}
<h2>Courses</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
Notice that for the last column, the scaffolded code displays the Name property of the Department entity that's loaded
into the Department navigation property:
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
Run the page (select the Courses tab on the Contoso University home page) to see the list with department names.
The list of instructors displays related data from the OfficeAssignment entity. The Instructor and
OfficeAssignment entities are in a one-to-zero-or-one relationship. You'll use eager loading for the
OfficeAssignment entities. As explained earlier, eager loading is typically more efficient when you need the related
data for all retrieved rows of the primary table. In this case, you want to display office assignments for all displayed
instructors.
When the user selects an instructor, related Course entities are displayed. The Instructor and Course entities are
In the ViewModels folder, create InstructorIndexData.cs and replace the existing code with the following code:
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; }
}
}
using ContosoUniversity.ViewModels;
The scaffolded code in the Index method specifies eager loading only for the OfficeAssignment navigation property:
Replace the Index method with the following code to load additional related data and put it in the view model:
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
return View(viewModel);
}
The method accepts optional route data (id) and a query string parameter (courseID) that provide the ID values of the
selected instructor and selected course, and passes all of the required data to the view. The parameters are provided by
the Select hyperlinks on the page.
Route data
Route data is data that the model binder found in a URL segment specified in the routing table. For example,
the default route specifies controller, action, and id segments:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
In the following URL, the default route maps Instructor as the controller, Index as the action and 1 as
the id; these are route data values.
http://localhost:1230/Instructor/Index/1?courseID=2021
"?courseID=2021" is a query string value. The model binder will also work if you pass the id as a query string
value:
http://localhost:1230/Instructor/Index?id=1&CourseID=2021
The URLs are created by ActionLink statements in the Razor view. In the following code, the id parameter
matches the default route, so id is added to the route data.
In the following code, courseID doesn't match a parameter in the default route, so it's added as a query
string.
The code begins by creating an instance of the view model and putting in it the list of instructors. The code specifies eager
The second Include method loads Courses, and for each Course that is loaded it does eager loading for the
Course.Department navigation property.
As mentioned previously, eager loading is not required but is done to improve performance. Since the view always
requires the OfficeAssignment entity, it's more efficient to fetch that in the same query. Course entities are required
when an instructor is selected in the web page, so eager loading is better than lazy loading only if the page is displayed
more often with a course selected than without.
If an instructor ID was selected, the selected instructor is retrieved from the list of instructors in the view model. The view
model's Courses property is then loaded with the Course entities from that instructor's Courses navigation property.
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID ==
id.Value).Single().Courses;
}
The Where method returns a collection, but in this case the criteria passed to that method result in only a single
Instructor entity being returned. The Single method converts the collection into a single Instructor entity, which
gives you access to that entity's Courses property.
Instead of:
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
@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>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
Added code that will dynamically add class="selectedrow" to the tr element of the selected instructor. This sets
a background color for the selected row using the CSS class that you created earlier. (The valign attribute will be
useful in the following tutorial when you add a multi-row column to the table.)
Added a new ActionLink labeled Select immediately before the other links in each row, which causes the selected
instructor ID to be sent to the Index method.
Run the application and select the Instructors tab. The page displays the Location property of related
In the Views\Instructor\Index.cshtml file, after the closing table element (at the end of the file), add the following
highlighted code. This displays a list of courses related to an instructor when an instructor is selected.
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
</tr>
}
</table>
</table>
}
This code reads the Courses property of the view model to display a list of courses. It also provides a Select hyperlink
that sends the ID of the selected course to the Index action method.
Note The .css file is cached by browsers. If you don't see the changes when you run the application,
do a hard refresh (hold down the CTRL key while clicking the Refresh button, or press CTRL+F5).
Run the page and select an instructor. Now you see a grid that displays courses assigned to the selected instructor, and
for each course you see the name of the assigned department.
This code reads the Enrollments property of the view model in order to display a list of students enrolled in the course.
Run the page and select an instructor. Then select a course to see the list of enrolled students and their grades.
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
When you retrieved the list of instructors, you specified eager loading for the Courses navigation property and for the
Department property of each course. Then you put the Courses collection in the view model, and now you're accessing
the Enrollments navigation property from one entity in that collection. Because you didn't specify eager loading for the
Course.Enrollments navigation property, the data from that property is appearing in the page as a result of lazy
loading.
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);
}
After getting the selected Course entity, the new code explicitly loads that course's Enrollments navigation property:
Notice that you use the Collection method to load a collection property, but for a property that holds just one entity,
you use the Reference method. You can run the Instructor Index page now and you'll see no difference in what's
Summary
You've now used all three ways (lazy, eager, and explicit) to load related data into navigation properties. In the next
tutorial you'll learn how to update related data.
Links to other Entity Framework resources can be found in the ASP.NET Data Access Content Map (/whitepapers/aspnet-
data-access-content-map) .
Author Information
Tom Dykstra – Tom Dykstra is a Senior Programming Writer on Microsoft's Web Platform &
Tools Content Team...
Comments (34)
This site is managed for Microsoft by Neudesic, LLC. | © 2015 Microsoft. All rights reserved.