Introdução com o Entity Framework 4.0 Database First e ASP.NET 4 Web Forms – Parte 4
por Tom Dykstra
O aplicativo Web de exemplo da Contoso University demonstra como criar aplicativos ASP.NET Web Forms usando o Entity Framework 4.0 e o Visual Studio 2010. Para obter informações sobre a série de tutoriais, consulte o primeiro tutorial da série
Trabalhando com dados relacionados
No tutorial anterior, você usou o EntityDataSource
controle para filtrar, classificar e agrupar dados. Neste tutorial, você exibirá e atualizará os dados relacionados.
Você criará a página Instrutores que mostra uma lista de instrutores. Ao selecionar um instrutor, você verá uma lista de cursos ministrados por esse instrutor. Ao selecionar um curso, você verá detalhes do curso e uma lista de alunos inscritos no curso. Você pode editar o nome do instrutor, a data de contratação e a atribuição do escritório. A atribuição do office é um conjunto de entidades separado que você acessa por meio de uma propriedade de navegação.
Você pode vincular master dados para detalhar dados na marcação ou no código. Nesta parte do tutorial, você usará os dois métodos.
Exibindo e atualizando entidades relacionadas em um controle GridView
Crie uma nova página da Web chamada Instructors.aspx que usa a página master Site.Master e adicione a seguinte marcação ao Content
controle chamado Content2
:
<h2>Instructors</h2>
<div>
<asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="People"
Where="it.HireDate is not null" Include="OfficeAssignment" EnableUpdate="True">
</asp:EntityDataSource>
</div>
Essa marcação cria um EntityDataSource
controle que seleciona instrutores e habilita atualizações. O div
elemento configura a marcação a ser renderizada à esquerda para que você possa adicionar uma coluna à direita mais tarde.
Entre a EntityDataSource
marcação e a marca de fechamento </div>
, adicione a seguinte marcação que cria um GridView
controle e um Label
controle que você usará para mensagens de erro:
<asp:GridView ID="InstructorsGridView" runat="server" AllowPaging="True" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="PersonID" DataSourceID="InstructorsEntityDataSource"
OnSelectedIndexChanged="InstructorsGridView_SelectedIndexChanged"
SelectedRowStyle-BackColor="LightGray"
onrowupdating="InstructorsGridView_RowUpdating">
<Columns>
<asp:CommandField ShowSelectButton="True" ShowEditButton="True" />
<asp:TemplateField HeaderText="Name" SortExpression="LastName">
<ItemTemplate>
<asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("LastName") %>'></asp:Label>,
<asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("FirstMidName") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="InstructorLastNameTextBox" runat="server" Text='<%# Bind("FirstMidName") %>' Width="7em"></asp:TextBox>
<asp:TextBox ID="InstructorFirstNameTextBox" runat="server" Text='<%# Bind("LastName") %>' Width="7em"></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Hire Date" SortExpression="HireDate">
<ItemTemplate>
<asp:Label ID="InstructorHireDateLabel" runat="server" Text='<%# Eval("HireDate", "{0:d}") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="InstructorHireDateTextBox" runat="server" Text='<%# Bind("HireDate", "{0:d}") %>' Width="7em"></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Office Assignment" SortExpression="OfficeAssignment.Location">
<ItemTemplate>
<asp:Label ID="InstructorOfficeLabel" runat="server" Text='<%# Eval("OfficeAssignment.Location") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox ID="InstructorOfficeTextBox" runat="server"
Text='<%# Eval("OfficeAssignment.Location") %>' Width="7em"
oninit="InstructorOfficeTextBox_Init"></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
<SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
</asp:GridView>
<asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label>
Esse GridView
controle habilita a seleção de linhas, realça a linha selecionada com uma cor de tela de fundo cinza claro e especifica manipuladores (que você criará posteriormente) para os SelectedIndexChanged
eventos e Updating
. Ele também especifica PersonID
para a DataKeyNames
propriedade , para que o valor da chave da linha selecionada possa ser passado para outro controle que você adicionará posteriormente.
A última coluna contém a atribuição de escritório do instrutor, que é armazenada em uma propriedade de navegação da Person
entidade porque ela vem de uma entidade associada. Observe que o EditItemTemplate
elemento especifica Eval
em vez de Bind
, porque o GridView
controle não pode associar diretamente às propriedades de navegação para atualizá-las. Você atualizará a atribuição do escritório em código. Para fazer isso, você precisará de uma referência ao TextBox
controle e obterá e salvará isso no TextBox
evento do Init
controle.
Seguindo o GridView
controle está um Label
controle usado para mensagens de erro. A propriedade do controle é false
e o estado de Visible
exibição está desativado, para que o rótulo apareça somente quando o código o tornar visível em resposta a um erro.
Abra o arquivo Instructors.aspx.cs e adicione a seguinte using
instrução:
using ContosoUniversity.DAL;
Adicione um campo de classe privada imediatamente após a declaração de nome de classe parcial para manter uma referência à caixa de texto de atribuição de escritório.
private TextBox instructorOfficeTextBox;
Adicione um stub para o manipulador de eventos para o SelectedIndexChanged
qual você adicionará código posteriormente. Adicione também um manipulador para o evento do controle de atribuição TextBox
do Init
office para que você possa armazenar uma referência ao TextBox
controle. Você usará essa referência para obter o valor inserido pelo usuário para atualizar a entidade associada à propriedade de navegação.
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
}
protected void InstructorOfficeTextBox_Init(object sender, EventArgs e)
{
instructorOfficeTextBox = sender as TextBox;
}
Você usará o GridView
evento do Updating
controle para atualizar a Location
propriedade da entidade associada OfficeAssignment
. Adicione o seguinte manipulador para o Updating
evento:
protected void InstructorsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorBeingUpdated = Convert.ToInt32(e.Keys[0]);
var officeAssignment = (from o in context.OfficeAssignments
where o.InstructorID == instructorBeingUpdated
select o).FirstOrDefault();
try
{
if (String.IsNullOrWhiteSpace(instructorOfficeTextBox.Text) == false)
{
if (officeAssignment == null)
{
context.OfficeAssignments.AddObject(OfficeAssignment.CreateOfficeAssignment(instructorBeingUpdated, instructorOfficeTextBox.Text, null));
}
else
{
officeAssignment.Location = instructorOfficeTextBox.Text;
}
}
else
{
if (officeAssignment != null)
{
context.DeleteObject(officeAssignment);
}
}
context.SaveChanges();
}
catch (Exception)
{
e.Cancel = true;
ErrorMessageLabel.Visible = true;
ErrorMessageLabel.Text = "Update failed.";
//Add code to log the error.
}
}
}
Esse código é executado quando o usuário clica em Atualizar em uma GridView
linha. O código usa LINQ to Entities para recuperar a OfficeAssignment
entidade associada à entidade atualPerson
, usando a PersonID
da linha selecionada do argumento de evento.
Em seguida, o código executa uma das seguintes ações dependendo do valor no InstructorOfficeTextBox
controle:
- Se a caixa de texto tiver um valor e não
OfficeAssignment
houver nenhuma entidade a ser atualizada, ela criará uma. - Se a caixa de texto tiver um valor e houver uma
OfficeAssignment
entidade, ela atualizará o valor daLocation
propriedade. - Se a caixa de texto estiver vazia e uma
OfficeAssignment
entidade existir, ela excluirá a entidade.
Depois disso, ele salva as alterações no banco de dados. Se ocorrer uma exceção, ela exibirá uma mensagem de erro.
Execute a página.
Clique em Editar e todos os campos são alterados para caixas de texto.
Altere qualquer um desses valores, incluindo Atribuição do Office. Clique em Atualizar e você verá as alterações refletidas na lista.
Exibindo entidades relacionadas em um controle separado
Cada instrutor pode ministrar um ou mais cursos, portanto, você adicionará um EntityDataSource
controle e um GridView
controle para listar os cursos associados a qualquer instrutor selecionado no controle de instrutores GridView
. Para criar um título e o EntityDataSource
controle para entidades de cursos, adicione a seguinte marcação entre o controle de mensagem Label
de erro e a marca de fechamento </div>
:
<h3>Courses Taught</h3>
<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="Courses"
Where="@PersonID IN (SELECT VALUE instructor.PersonID FROM it.People AS instructor)">
<WhereParameters>
<asp:ControlParameter ControlID="InstructorsGridView" Type="Int32" Name="PersonID" PropertyName="SelectedValue" />
</WhereParameters>
</asp:EntityDataSource>
O Where
parâmetro contém o valor do PersonID
do instrutor cuja linha está selecionada no InstructorsGridView
controle . A Where
propriedade contém um comando de subseleção que obtém todas as entidades associadas Person
da propriedade de navegação de People
uma Course
entidade e seleciona a Course
entidade somente se uma das entidades associadas Person
contiver o valor selecionadoPersonID
.
Para criar o GridView
controle., adicione a seguinte marcação imediatamente após o CoursesEntityDataSource
controle (antes da marca de fechamento </div>
):
<asp:GridView ID="CoursesGridView" runat="server"
DataSourceID="CoursesEntityDataSource"
AllowSorting="True" AutoGenerateColumns="False"
SelectedRowStyle-BackColor="LightGray"
DataKeyNames="CourseID">
<EmptyDataTemplate>
<p>No courses found.</p>
</EmptyDataTemplate>
<Columns>
<asp:CommandField ShowSelectButton="True" />
<asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
<asp:TemplateField HeaderText="Department" SortExpression="DepartmentID">
<ItemTemplate>
<asp:Label ID="GridViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Como nenhum curso será exibido se nenhum instrutor for selecionado, um EmptyDataTemplate
elemento será incluído.
Execute a página.
Selecione um instrutor que tenha um ou mais cursos atribuídos e o curso ou cursos aparecem na lista. (Observação: embora o esquema de banco de dados permita vários cursos, nos dados de teste fornecidos com o banco de dados nenhum instrutor realmente tem mais de um curso. Você pode adicionar cursos ao banco de dados por conta própria usando a janela Servidor Explorer ou a página CoursesAdd.aspx, que você adicionará em um tutorial posterior.)
O CoursesGridView
controle mostra apenas alguns campos de curso. Para exibir todos os detalhes de um curso, você usará um DetailsView
controle para o curso selecionado pelo usuário. Em Instructors.aspx, adicione a marcação a seguir após a marca de fechamento </div>
(certifique-se de colocar essa marcação após a marca div de fechamento, não antes dela):
<div>
<h3>Course Details</h3>
<asp:EntityDataSource ID="CourseDetailsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="Courses"
AutoGenerateWhereClause="False" Where="it.CourseID = @CourseID" Include="Department,OnlineCourse,OnsiteCourse,StudentGrades.Person"
OnSelected="CourseDetailsEntityDataSource_Selected">
<WhereParameters>
<asp:ControlParameter ControlID="CoursesGridView" Type="Int32" Name="CourseID" PropertyName="SelectedValue" />
</WhereParameters>
</asp:EntityDataSource>
<asp:DetailsView ID="CourseDetailsView" runat="server" AutoGenerateRows="False"
DataSourceID="CourseDetailsEntityDataSource">
<EmptyDataTemplate>
<p>
No course selected.</p>
</EmptyDataTemplate>
<Fields>
<asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
<asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
<asp:TemplateField HeaderText="Department">
<ItemTemplate>
<asp:Label ID="DetailsViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Location">
<ItemTemplate>
<asp:Label ID="LocationLabel" runat="server" Text='<%# Eval("OnsiteCourse.Location") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="URL">
<ItemTemplate>
<asp:Label ID="URLLabel" runat="server" Text='<%# Eval("OnlineCourse.URL") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
</div>
Essa marcação cria um EntityDataSource
controle associado ao Courses
conjunto de entidades. A Where
propriedade seleciona um curso usando o CourseID
valor da linha selecionada no controle de cursos GridView
. A marcação especifica um manipulador para o Selected
evento, que você usará posteriormente para exibir as notas dos alunos, que é outro nível mais baixo na hierarquia.
Em Instructors.aspx.cs, crie o stub a seguir para o CourseDetailsEntityDataSource_Selected
método . (Você preencherá este stub posteriormente no tutorial; por enquanto, você precisará dele para que a página seja compilada e executada.)
protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}
Execute a página.
Inicialmente, não há detalhes do curso porque nenhum curso está selecionado. Selecione um instrutor que tenha um curso atribuído e selecione um curso para ver os detalhes.
Usando o evento EntityDataSource "Selected" para exibir dados relacionados
Por fim, você deseja mostrar todos os alunos inscritos e suas notas para o curso selecionado. Para fazer isso, você usará o Selected
evento do EntityDataSource
controle associado ao curso DetailsView
.
Em Instructors.aspx, adicione a seguinte marcação após o DetailsView
controle:
<h3>Student Grades</h3>
<asp:ListView ID="GradesListView" runat="server">
<EmptyDataTemplate>
<p>No student grades found.</p>
</EmptyDataTemplate>
<LayoutTemplate>
<table border="1" runat="server" id="itemPlaceholderContainer">
<tr runat="server">
<th runat="server">
Name
</th>
<th runat="server">
Grade
</th>
</tr>
<tr id="itemPlaceholder" runat="server">
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label ID="StudentLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>' />,
<asp:Label ID="StudentFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>' />
</td>
<td>
<asp:Label ID="StudentGradeLabel" runat="server" Text='<%# Eval("Grade") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
Essa marcação cria um ListView
controle que exibe uma lista de alunos e suas notas para o curso selecionado. Nenhuma fonte de dados é especificada porque você associará o controle no código. O EmptyDataTemplate
elemento fornece uma mensagem a ser exibida quando nenhum curso é selecionado. Nesse caso, não há alunos a serem exibidos. O LayoutTemplate
elemento cria uma tabela HTML para exibir a lista e especifica as ItemTemplate
colunas a serem exibidas. A ID do aluno e a nota do aluno são da StudentGrade
entidade e o nome do aluno é da Person
entidade que o Entity Framework disponibiliza na Person
propriedade de navegação da StudentGrade
entidade.
Em Instructors.aspx.cs, substitua o método stubbed-out CourseDetailsEntityDataSource_Selected
pelo seguinte código:
protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
var course = e.Results.Cast<Course>().FirstOrDefault();
if (course != null)
{
var studentGrades = course.StudentGrades.ToList();
GradesListView.DataSource = studentGrades;
GradesListView.DataBind();
}
}
O argumento de evento para esse evento fornece os dados selecionados na forma de uma coleção, que terá zero itens se nada for selecionado ou um item se uma Course
entidade for selecionada. Se uma Course
entidade for selecionada, o código usará o First
método para converter a coleção em um único objeto. Em seguida, ele obtém StudentGrade
entidades da propriedade de navegação, converte-as em uma coleção e associa o GradesListView
controle à coleção.
Isso é suficiente para exibir notas, mas você deseja garantir que a mensagem no modelo de dados vazio seja exibida na primeira vez que a página for exibida e sempre que um curso não for selecionado. Para fazer isso, crie o seguinte método, que você chamará de dois locais:
private void ClearStudentGradesDataSource()
{
var emptyStudentGradesList = new List<StudentGrade>();
GradesListView.DataSource = emptyStudentGradesList;
GradesListView.DataBind();
}
Chame esse novo método do Page_Load
método para exibir o modelo de dados vazio na primeira vez que a página for exibida. E chame-o do InstructorsGridView_SelectedIndexChanged
método porque esse evento é gerado quando um instrutor é selecionado, o que significa que novos cursos são carregados no controle de cursos GridView
e nenhum é selecionado ainda. Aqui estão as duas chamadas:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ClearStudentGradesDataSource();
}
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
ClearStudentGradesDataSource();
}
Execute a página.
Selecione um instrutor que tenha um curso atribuído e selecione o curso.
Agora você viu algumas maneiras de trabalhar com dados relacionados. No tutorial a seguir, você aprenderá a adicionar relações entre entidades existentes, como remover relações e como adicionar uma nova entidade que tenha uma relação com uma entidade existente.