Lendo dados relacionados com o Entity Framework em um aplicativo MVC ASP.NET (5 de 10)
por Tom Dykstra
O aplicativo Web de exemplo da Contoso University demonstra como criar ASP.NET aplicativos MVC 4 usando o Entity Framework 5 Code First e o Visual Studio 2012. Para obter informações sobre a série de tutoriais, consulte o primeiro tutorial da série.
Observação
Se você tiver um problema, não poderá resolve, baixe o capítulo concluído e tente reproduzir o problema. Geralmente, você pode encontrar a solução para o problema comparando seu código com o código concluído. Para obter alguns erros comuns e como resolvê-los, consulte Erros e soluções alternativas.
No tutorial anterior, você concluiu o modelo de dados Da escola. Neste tutorial, você lerá e exibirá dados relacionados, ou seja, dados que o Entity Framework carrega nas propriedades de navegação.
As ilustrações a seguir mostram as páginas com as quais você trabalhará.
Carregamento lento, ansioso e explícito de dados relacionados
Há várias maneiras pelas quais o Entity Framework pode carregar dados relacionados nas propriedades de navegação de uma entidade:
Carregamento lento. Quando a entidade é lida pela primeira vez, os dados relacionados não são recuperados. No entanto, na primeira vez que você tenta acessar uma propriedade de navegação, os dados necessários para essa propriedade de navegação são recuperados automaticamente. Isso resulta em várias consultas enviadas para o banco de dados — uma para a própria entidade e outra para cada vez que os dados relacionados para a entidade devem ser recuperados.
Carregamento adiantado. Quando a entidade é lida, os dados relacionados são recuperados com ela. Normalmente, isso resulta em uma única consulta de junção que recupera todos os dados necessários. Especifique o carregamento ansioso usando o
Include
método .Carregamento explícito. Isso é semelhante ao carregamento lento, exceto que você recupera explicitamente os dados relacionados no código; isso não acontece automaticamente quando você acessa uma propriedade de navegação. Você carrega dados relacionados manualmente obtendo a entrada do gerenciador de estado do objeto para uma entidade e chamando o
Collection.Load
método para coleções ou oReference.Load
método para propriedades que contêm uma única entidade. (No exemplo a seguir, se você quisesse carregar a propriedade de navegação Administrador, substituiriaCollection(x => x.Courses)
porReference(x => x.Administrator)
.)
Como eles não recuperam imediatamente os valores de propriedade, o carregamento lento e o carregamento explícito também são conhecidos como carregamento adiado.
Em geral, se você souber que precisa de dados relacionados para cada entidade recuperada, o carregamento ansioso oferece o melhor desempenho, pois uma única consulta enviada ao banco de dados normalmente é mais eficiente do que consultas separadas para cada entidade recuperada. Por exemplo, nos exemplos acima, suponha que cada departamento tenha dez cursos relacionados. O exemplo de carregamento ansioso resultaria em apenas uma única consulta (junção) e uma única viagem de ida e volta ao banco de dados. Os exemplos de carregamento lento e explícito resultariam em onze consultas e onze viagens de ida e volta ao banco de dados. As viagens de ida e volta extras para o banco de dados são especialmente prejudiciais ao desempenho quando a latência é alta.
Por outro lado, em alguns cenários, o carregamento lento é mais eficiente. O carregamento ansioso pode fazer com que uma junção muito complexa seja gerada, o que SQL Server não pode processar com eficiência. Ou se você precisar acessar as propriedades de navegação de uma entidade somente para um subconjunto de um conjunto de entidades que você está processando, o carregamento lento pode ter um desempenho melhor, pois o carregamento ansioso recuperaria mais dados do que você precisa. Se o desempenho for crítico, será melhor testar o desempenho das duas maneiras para fazer a melhor escolha.
Normalmente, você usaria o carregamento explícito somente quando desativasse o carregamento lento. Um cenário em que você deve desativar o carregamento lento é durante a serialização. O carregamento lento e a serialização não se misturam bem e, se você não tiver cuidado, poderá acabar consultando significativamente mais dados do que você pretendia quando o carregamento lento estiver habilitado. A serialização geralmente funciona acessando cada propriedade em uma instância de um tipo. O acesso à propriedade dispara o carregamento lento e essas entidades carregadas lentas são serializadas. Em seguida, o processo de serialização acessa cada propriedade das entidades carregadas lentamente, potencialmente causando carregamento e serialização ainda mais lentos. Para evitar essa reação em cadeia de fuga, desative o carregamento lento antes de serializar uma entidade.
A classe de contexto do banco de dados executa o carregamento lento por padrão. Há duas maneiras de desabilitar o carregamento lento:
Para propriedades de navegação específicas, omita o
virtual
palavra-chave ao declarar a propriedade.Para todas as propriedades de navegação, defina
LazyLoadingEnabled
comofalse
. Por exemplo, você pode colocar o seguinte código no construtor da classe de contexto:this.Configuration.LazyLoadingEnabled = false;
O carregamento lento pode mascarar o código que causa problemas de desempenho. Por exemplo, o código que não especifica carregamentos ansiosos ou explícitos, mas processa um alto volume de entidades e usa várias propriedades de navegação em cada iteração pode ser muito ineficiente (devido a muitas viagens de ida e volta ao banco de dados). Um aplicativo que tem um bom desempenho no desenvolvimento usando um SQL Server local pode ter problemas de desempenho quando movido para SQL do Azure Banco de Dados devido ao aumento da latência e ao carregamento lento. A criação de perfil das consultas de banco de dados com uma carga de teste realista ajudará você a determinar se o carregamento lento é apropriado. Para obter mais informações, consulte Desmistificando estratégias do Entity Framework: carregando dados relacionados e usando o Entity Framework para reduzir a latência de rede para SQL Azure.
Criar uma página de índice de cursos que exibe o nome do departamento
A entidade Course
inclui uma propriedade de navegação que contém a entidade Department
do departamento ao qual o curso é atribuído. Para exibir o nome do departamento atribuído em uma lista de cursos, você precisa obter a Name
propriedade da Department
entidade que está na Course.Department
propriedade de navegação.
Crie um controlador chamado CourseController
para o Course
tipo de entidade, usando as mesmas opções que você fez anteriormente para o Student
controlador, conforme mostrado na ilustração a seguir (exceto ao contrário da imagem, sua classe de contexto está no namespace da DAL, não no namespace Modelos):
Abra Controllers\CourseController.cs e examine o Index
método :
public ViewResult Index()
{
var courses = db.Courses.Include(c => c.Department);
return View(courses.ToList());
}
O scaffolding automático especificou o carregamento adiantado para a propriedade de navegação Department
usando o método Include
.
Abra Views\Course\Index.cshtml e substitua o código existente pelo código a seguir. As alterações são realçadas:
@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>
Você fez as seguintes alterações no código gerado por scaffolding:
- O cabeçalho mudou de Índice para Cursos.
- Moveu os links de linha para a esquerda.
- Adicionada uma coluna sob o título Número que mostra o valor da
CourseID
propriedade. (Por padrão, as chaves primárias não são scaffolded porque normalmente não têm sentido para os usuários finais. No entanto, nesse caso, a chave primária é significativa e você deseja mostrá-la.) - Alterou o título da última coluna de DepartmentID (o nome da chave estrangeira para a
Department
entidade) para Departamento.
Observe que, para a última coluna, o código scaffolded exibe a Name
propriedade da Department
entidade carregada na Department
propriedade de navegação:
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
Execute a página (selecione a guia Cursos na home page da Contoso University) para ver a lista com nomes de departamento.
Criar uma página de índice de instrutores que mostra cursos e inscrições
Nesta seção, você criará um controlador e uma exibição para a Instructor
entidade para exibir a página Índice de Instrutores:
Essa página lê e exibe dados relacionados das seguintes maneiras:
- A lista de instrutores exibe dados relacionados da entidade
OfficeAssignment
. As entidadesInstructor
eOfficeAssignment
estão em uma relação um para zero ou um. Você usará o carregamento adiantado para as entidadesOfficeAssignment
. Conforme explicado anteriormente, o carregamento adiantado é geralmente mais eficiente quando você precisa dos dados relacionados para todas as linhas recuperadas da tabela primária. Nesse caso, você deseja exibir atribuições de escritório para todos os instrutores exibidos. - Quando o usuário seleciona um instrutor, as entidades
Course
relacionadas são exibidas. As entidadesInstructor
eCourse
estão em uma relação muitos para muitos. O carregamento adiantado é usado para as entidadesCourse
e suas entidadesDepartment
relacionadas. Nesse caso, o carregamento lento pode ser mais eficiente porque você precisa de cursos apenas para o instrutor selecionado. No entanto, este exemplo mostra como usar o carregamento adiantado para propriedades de navegação em entidades que estão nas propriedades de navegação. - Quando o usuário seleciona um curso, os dados relacionados do conjunto de entidades
Enrollments
são exibidos. As entidadesCourse
eEnrollment
estão em uma relação um-para-muitos. Você adicionará o carregamento explícito paraEnrollment
entidades e suas entidades relacionadasStudent
. (O carregamento explícito não é necessário porque o carregamento lento está habilitado, mas isso mostra como fazer o carregamento explícito.)
Criar um modelo de exibição para a exibição de índice de instrutor
A página Índice de Instrutor mostra três tabelas diferentes. Portanto, você criará um modelo de exibição que inclui três propriedades, cada uma contendo os dados de uma das tabelas.
Na pasta ViewModels , crie InstructorIndexData.cs e substitua o código existente pelo seguinte código:
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; }
}
}
Adicionando um estilo para linhas selecionadas
Para marcar as linhas selecionadas, você precisa de uma cor de plano de fundo diferente. Para fornecer um estilo para essa interface do usuário, adicione o seguinte código realçado à seção /* info and errors */
em Content\Site.css, conforme mostrado abaixo:
/* info and errors */
.selectedrow
{
background-color: #a4d4e6;
}
.message-info {
border: 1px solid;
clear: both;
padding: 10px 20px;
}
Criando o controlador e as exibições do instrutor
Crie um InstructorController
controlador, conforme mostrado na ilustração a seguir:
Abra Controllers\InstructorController.cs e adicione uma using
instrução para o ViewModels
namespace:
using ContosoUniversity.ViewModels;
O código scaffolded no método especifica o Index
carregamento adiantado somente para a OfficeAssignment
propriedade de navegação:
public ViewResult Index()
{
var instructors = db.Instructors.Include(i => i.OfficeAssignment);
return View(instructors.ToList());
}
Substitua o Index
método pelo seguinte código para carregar dados relacionados adicionais e colocá-lo no modelo de exibição:
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);
}
O método aceita dados de rota opcionais (id
) e um parâmetro de cadeia de caracteres de consulta (courseID
) que fornecem os valores de ID do instrutor selecionado e do curso selecionado e passa todos os dados necessários para a exibição. Os parâmetros são fornecidos pelos hiperlinks Selecionar na página.
Dica
Rotear dados
Dados de rota são dados que o associador de modelo encontrou em um segmento de URL especificado na tabela de roteamento. Por exemplo, a rota padrão especifica controller
os segmentos , action
e id
:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Na URL a seguir, a rota padrão é mapeada Instructor
como , controller
Index
como e action
1 como ; id
esses são valores de dados de rota.
http://localhost:1230/Instructor/Index/1?courseID=2021
"?courseID=2021" é um valor de cadeia de caracteres de consulta. O associador de modelo também funcionará se você passar o id
como um valor de cadeia de caracteres de consulta:
http://localhost:1230/Instructor/Index?id=1&CourseID=2021
As URLs são criadas por ActionLink
instruções no modo de exibição Razor. No código a seguir, o id
parâmetro corresponde à rota padrão, portanto id
, é adicionado aos dados de rota.
@Html.ActionLink("Select", "Index", new { id = item.PersonID })
No código a seguir, courseID
não corresponde a um parâmetro na rota padrão, portanto, ele é adicionado como uma cadeia de caracteres de consulta.
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
O código começa com a criação de uma instância do modelo de exibição e colocando-a na lista de instrutores. O código especifica o carregamento adiantado para a Instructor.OfficeAssignment
propriedade de navegação e 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);
O segundo Include
método carrega Cursos e, para cada Curso carregado, ele faz o carregamento adiantado para a Course.Department
propriedade de navegação.
.Include(i => i.Courses.Select(c => c.Department))
Conforme mencionado anteriormente, o carregamento adiantado não é necessário, mas é feito para melhorar o desempenho. Como a exibição sempre exige a entidade OfficeAssignment
, é mais eficiente buscar isso na mesma consulta. Course
as entidades são necessárias quando um instrutor é selecionado na página da Web, portanto, o carregamento adiantado é melhor do que o carregamento lento somente se a página for exibida com mais frequência com um curso selecionado do que sem.
Se uma ID de instrutor tiver sido selecionada, o instrutor selecionado será recuperado da lista de instrutores no modelo de exibição. Em seguida, a propriedade Courses
do modelo de exibição é carregada com as entidades Course
da propriedade de navegação Courses
desse instrutor.
if (id != null)
{
ViewBag.InstructorID = id.Value;
viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses;
}
O Where
método retorna uma coleção, mas nesse caso os critérios passados para esse método resultam em apenas uma única Instructor
entidade sendo retornada. O método Single
converte a coleção em uma única entidade Instructor
, que fornece acesso à propriedade Courses
dessa entidade.
Use o método Single em uma coleção quando souber que a coleção terá apenas um item. O Single
método gera uma exceção se a coleção passada para ela estiver vazia ou se houver mais de um item. Uma alternativa é SingleOrDefault, que retorna um valor padrão (null
nesse caso) se a coleção estiver vazia. No entanto, nesse caso, isso ainda resultaria em uma exceção (da tentativa de localizar uma Courses
propriedade em uma null
referência) e a mensagem de exceção indicaria menos claramente a causa do problema. Ao chamar o Single
método , você também pode passar a Where
condição em vez de chamar o Where
método separadamente:
.Single(i => i.InstructorID == id.Value)
Em vez de:
.Where(I => i.InstructorID == id.Value).Single()
Em seguida, se um curso foi selecionado, o curso selecionado é recuperado na lista de cursos no modelo de exibição. Em seguida, a propriedade do modelo de Enrollments
exibição é carregada com as Enrollment
entidades da propriedade de navegação desse Enrollments
curso.
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
Modificando a exibição de índice do instrutor
Em Views\Instructor\Index.cshtml, substitua o código existente pelo código a seguir. As alterações são realçadas:
@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>
Você fez as seguintes alterações no código existente:
Alterou a classe de modelo para
InstructorIndexData
.Alterou o título de página de Índice para Instrutores.
Moveu as colunas de link de linha para a esquerda.
A coluna FullName foi removida.
Adicionada uma coluna do Office que será
item.OfficeAssignment.Location
exibida somente seitem.OfficeAssignment
não for nula. (Como essa é uma relação de um para zero ou um, pode não haver uma entidade relacionadaOfficeAssignment
.)<td> @if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location } </td>
Código adicionado que adicionará dinamicamente
class="selectedrow"
aotr
elemento do instrutor selecionado. Isso define uma cor da tela de fundo para a linha selecionada usando a classe CSS que você criou anteriormente. (Ovalign
atributo será útil no tutorial a seguir quando você adicionar uma coluna de várias linhas à tabela.)string selectedRow = ""; if (item.InstructorID == ViewBag.InstructorID) { selectedRow = "selectedrow"; } <tr class="@selectedRow" valign="top">
Adicionada uma nova
ActionLink
rotulada Selecionar imediatamente antes dos outros links em cada linha, o que faz com que a ID do instrutor selecionada seja enviada para oIndex
método .
Execute o aplicativo e selecione a guia Instrutores . A página exibe a Location
propriedade de entidades relacionadas OfficeAssignment
e uma célula de tabela vazia quando não há nenhuma entidade relacionada OfficeAssignment
.
No arquivo Views\Instructor\Index.cshtml , após o elemento de fechamento table
(no final do arquivo), adicione o código realçado a seguir. Isso exibe uma lista de cursos relacionados a um instrutor quando um instrutor é selecionado.
<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>
}
Esse código lê a propriedade Courses
do modelo de exibição para exibir uma lista de cursos. Ele também fornece um Select
hiperlink que envia a ID do curso selecionado para o método de Index
ação.
Observação
O arquivo .css é armazenado em cache por navegadores. Se você não vir as alterações ao executar o aplicativo, faça uma atualização rígida (mantenha pressionada a tecla CTRL ao clicar no botão Atualizar ou pressione CTRL+F5).
Execute a página e selecione um instrutor. Agora, você verá uma grade que exibe os cursos atribuídos ao instrutor selecionado, e para cada curso, verá o nome do departamento atribuído.
Após o bloco de código que você acabou de adicionar, adicione o código a seguir. Isso exibe uma lista dos alunos que estão registrados em um curso quando esse curso é selecionado.
@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>
}
Esse código lê a propriedade Enrollments
do modelo de exibição para exibir uma lista dos alunos matriculados no curso.
Execute a página e selecione um instrutor. Em seguida, selecione um curso para ver a lista de alunos registrados e suas notas.
Adicionando carregamento explícito
Abra InstructorController.cs e veja como o Index
método obtém a lista de registros de um curso selecionado:
if (courseID != null)
{
ViewBag.CourseID = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
Ao recuperar a lista de instrutores, você especificou o carregamento adiantado para a Courses
propriedade de navegação e para a Department
propriedade de cada curso. Em seguida, você coloca a Courses
coleção no modelo de exibição e agora está acessando a Enrollments
propriedade de navegação de uma entidade nessa coleção. Como você não especificou o carregamento adiantado para a Course.Enrollments
propriedade de navegação, os dados dessa propriedade são exibidos na página como resultado do carregamento lento.
Se você desabilitou o carregamento lento sem alterar o código de outra forma, a Enrollments
propriedade seria nula, independentemente de quantos registros o curso realmente tinha. Nesse caso, para carregar a Enrollments
propriedade, você teria que especificar carregamento adiantado ou carregamento explícito. Você já viu como fazer carregamento adiantado. Para ver um exemplo de carregamento explícito, substitua o Index
método pelo código a seguir, que carrega explicitamente a Enrollments
propriedade . O código alterado está realçado.
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);
}
Depois de obter a entidade selecionada Course
, o novo código carrega explicitamente a propriedade de navegação do Enrollments
curso:
db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
Em seguida, ele carrega explicitamente a entidade relacionada Student
de cada Enrollment
entidade:
db.Entry(enrollment).Reference(x => x.Student).Load();
Observe que você usa o Collection
método para carregar uma propriedade de coleção, mas para uma propriedade que contém apenas uma entidade, use o Reference
método . Você pode executar a página Índice de Instrutor agora e não verá nenhuma diferença no que é exibido na página, embora tenha alterado a forma como os dados são recuperados.
Resumo
Agora você usou todas as três maneiras (lentas, ansiosas e explícitas) para carregar dados relacionados em propriedades de navegação. No próximo tutorial, você aprenderá a atualizar dados relacionados.
Os links para outros recursos do Entity Framework podem ser encontrados no Mapa de Conteúdo de Acesso a Dados do ASP.NET.