Introducción a Entity Framework 4.0 Database First y ASP.NET 4 Web Forms: parte 4
Por Tom Dykstra
En la aplicación web de ejemplo Contoso University se muestra cómo crear aplicaciones ASP.NET Web Forms con Entity Framework 4.0 y Visual Studio 2010. Para obtener información sobre la serie de tutoriales, consulte el primer tutorial de la serie
Trabajar con datos relacionados
En el tutorial anterior usó el EntityDataSource
control para filtrar, ordenar y agrupar datos. En este tutorial, mostrará y actualizará los datos relacionados.
Creará la página Instructors que muestra una lista de instructores. Al seleccionar un instructor, verá una lista de los cursos que enseña ese instructor. Al seleccionar un curso, verá los detalles del curso y una lista de alumnos inscritos en el curso. Puede editar el nombre del instructor, la fecha de contratación y la asignación de oficina. La asignación de office es un conjunto de entidades independiente al que se accede a través de una propiedad de navegación.
Puede vincular datos maestros a datos detallados en el marcado o en el código. En esta parte del tutorial, usará ambos métodos.
Mostrar y actualizar entidades relacionadas en un control GridView
Cree una nueva página web denominada Instructors.aspx que use la página maestra Site.Master y agregue el marcado siguiente al Content
control denominado 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>
Este marcado crea un control EntityDataSource
que selecciona instructores y habilita las actualizaciones. El div
elemento configura el marcado para que se represente a la izquierda para poder agregar una columna a la derecha más adelante.
Entre el EntityDataSource
marcado y la etiqueta de cierre </div>
, agregue el marcado siguiente que crea un GridView
control y un Label
control que usará para los mensajes de error:
<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>
Este GridView
control habilita la selección de filas, resalta la fila seleccionada con un color de fondo gris claro y especifica los controladores (que creará más adelante) para los SelectedIndexChanged
eventos y Updating
. También especifica PersonID
para la DataKeyNames
propiedad, de modo que el valor de clave de la fila seleccionada se pueda pasar a otro control que agregará más adelante.
La última columna contiene la asignación de oficina del instructor, que se almacena en una propiedad de navegación de la Person
entidad porque procede de una entidad asociada. Observe que el EditItemTemplate
elemento especifica Eval
en lugar de Bind
, ya que el GridView
control no puede enlazar directamente a las propiedades de navegación para actualizarlos. Actualizará la asignación de office en el código. Para ello, necesitará una referencia al TextBox
control y la guardará en el TextBox
evento del Init
control.
Después del GridView
control es un Label
control que se usa para los mensajes de error. La propiedad del Visible
control es false
y el estado de vista está desactivado, de modo que la etiqueta solo aparecerá cuando el código lo haga visible en respuesta a un error.
Abra el archivo Instructors.aspx.cs y agregue la siguiente using
instrucción:
using ContosoUniversity.DAL;
Agregue un campo de clase privada inmediatamente después de la declaración de nombre de clase parcial para contener una referencia al cuadro de texto asignación de office.
private TextBox instructorOfficeTextBox;
Agregue un código auxiliar para el SelectedIndexChanged
controlador de eventos que agregará código para más adelante. Agregue también un controlador para el evento del control de asignación TextBox
de office Init
para que pueda almacenar una referencia al control TextBox
. Usará esta referencia para obtener el valor especificado por el usuario para actualizar la entidad asociada a la propiedad de navegación.
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
}
protected void InstructorOfficeTextBox_Init(object sender, EventArgs e)
{
instructorOfficeTextBox = sender as TextBox;
}
Usará el GridView
evento del Updating
control para actualizar la Location
propiedad de la entidad asociada OfficeAssignment
. Agregue el controlador siguiente para el evento Updating
:
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.
}
}
}
Este código se ejecuta cuando el usuario hace clic en Actualizar en una GridView
fila. El código usa LINQ to Entities para recuperar la OfficeAssignment
entidad asociada a la entidad actual Person
mediante el uso de PersonID
la fila seleccionada del argumento event.
A continuación, el código realiza una de las siguientes acciones en función del valor del controlInstructorOfficeTextBox
:
- Si el cuadro de texto tiene un valor y no hay ninguna
OfficeAssignment
entidad que actualizar, crea una. - Si el cuadro de texto tiene un valor y hay una entidad
OfficeAssignment
, actualiza el valor de la propiedadLocation
. - Si el cuadro de texto está vacío y existe una entidad
OfficeAssignment
, elimina la entidad.
Después de esto, guarda los cambios en la base de datos. Si se produce una excepción, muestra un mensaje de error.
Ejecute la página.
Haga clic en Editar y todos los campos cambien a cuadros de texto.
Cambie cualquiera de estos valores, incluida la asignación deOffice. Haga clic en Actualizar y verá los cambios reflejados en la lista.
Mostrar entidades relacionadas en un control independiente
Cada instructor puede enseñar uno o varios cursos, por lo que agregará un control EntityDataSource
y un GridView
control para enumerar los cursos asociados con el instructor que esté seleccionado en el control instructores GridView
. Para crear un encabezado y el EntityDataSource
control para las entidades courses, agregue el marcado siguiente entre el control de mensaje Label
de error y la etiqueta de cierre </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>
El parámetro Where
contiene el valor del instructor PersonID
cuya fila está seleccionada en el control InstructorsGridView
. La Where
propiedad contiene un comando de subselección que obtiene todas las entidades asociadas Person
de la propiedad de navegación de People
una Course
entidad y selecciona la Course
entidad solo si una de las entidades asociadas Person
contiene el valor seleccionado PersonID
.
Para crear el GridView
control, agregue el siguiente marcado inmediatamente después del CoursesEntityDataSource
control (antes de la etiqueta de cierre </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>
Dado que no se mostrará ningún curso si no se selecciona ningún instructor, se incluye un EmptyDataTemplate
elemento.
Ejecute la página.
Seleccione un instructor que tenga uno o varios cursos asignados y el curso o cursos aparecen en la lista. (Nota: aunque el esquema de la base de datos permite varios cursos, en los datos de prueba proporcionados con la base de datos no hay ningún instructor que realmente tenga más de un curso. Puede agregar cursos a la base de datos usted mismo mediante la ventana Explorador de servidores o la página CoursesAdd.aspx, que agregará en un tutorial posterior).
El CoursesGridView
control muestra solo algunos campos de curso. Para mostrar todos los detalles de un curso, usará un DetailsView
control para el curso que selecciona el usuario. En Instructors.aspx, agregue el siguiente marcado después de la etiqueta de cierre </div>
(asegúrese de colocar este marcado después de la etiqueta div de cierre, no antes de ella):
<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>
Este marcado crea un EntityDataSource
control enlazado al Courses
conjunto de entidades. La Where
propiedad selecciona un curso con el CourseID
valor de la fila seleccionada en el control courses GridView
. El marcado especifica un controlador para el Selected
evento, que usará más adelante para mostrar las calificaciones de los alumnos, que es otro nivel inferior en la jerarquía.
En Instructors.aspx.cs, cree el código auxiliar siguiente para el CourseDetailsEntityDataSource_Selected
método. (Rellenará este código auxiliar más adelante en el tutorial; por ahora, lo necesitará para que la página se compile y ejecute).
protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}
Ejecute la página.
Inicialmente no hay detalles del curso porque no se selecciona ningún curso. Seleccione un instructor que tenga un curso asignado y, a continuación, seleccione un curso para ver los detalles.
Uso del evento "Selected" de EntityDataSource para mostrar datos relacionados
Por último, desea mostrar todos los alumnos inscritos y sus calificaciones para el curso seleccionado. Para ello, usará el Selected
evento del EntityDataSource
control enlazado al curso DetailsView
.
En Instructors.aspx, agregue el marcado siguiente después del DetailsView
control :
<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>
Este marcado crea un ListView
control que muestra una lista de alumnos y sus calificaciones para el curso seleccionado. No se especifica ningún origen de datos porque enlazará el control en el código. El EmptyDataTemplate
elemento proporciona un mensaje para mostrar cuando no se selecciona ningún curso; en ese caso, no hay alumnos que se muestren. El LayoutTemplate
elemento crea una tabla HTML para mostrar la lista y ItemTemplate
especifica las columnas que se van a mostrar. El identificador de alumno y el grado de estudiante proceden de la StudentGrade
entidad y el nombre del alumno procede de la Person
entidad que Entity Framework pone a disposición en la Person
propiedad de navegación de la StudentGrade
entidad.
En Instructors.aspx.cs, reemplace el método de código auxiliar CourseDetailsEntityDataSource_Selected
por el código siguiente:
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();
}
}
El argumento event para este evento proporciona los datos seleccionados en forma de una colección, que tendrá cero elementos si no se selecciona nada o un elemento si se selecciona una Course
entidad. Si se selecciona una Course
entidad, el código usa el First
método para convertir la colección en un solo objeto. A continuación, obtiene StudentGrade
entidades de la propiedad de navegación, las convierte en una colección y enlaza el GradesListView
control a la colección.
Esto es suficiente para mostrar las calificaciones, pero quiere asegurarse de que el mensaje de la plantilla de datos vacía se muestra la primera vez que se muestra la página y siempre que no se seleccione un curso. Para ello, cree el método siguiente, al que llamará desde dos lugares:
private void ClearStudentGradesDataSource()
{
var emptyStudentGradesList = new List<StudentGrade>();
GradesListView.DataSource = emptyStudentGradesList;
GradesListView.DataBind();
}
Llame a este nuevo método desde el Page_Load
método para mostrar la plantilla de datos vacía la primera vez que se muestre la página. Y llámalo desde el InstructorsGridView_SelectedIndexChanged
método porque ese evento se genera cuando se selecciona un instructor, lo que significa que los nuevos cursos se cargan en el control de cursos GridView
y aún no se selecciona ninguno. Estas son las dos llamadas:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ClearStudentGradesDataSource();
}
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
ClearStudentGradesDataSource();
}
Ejecute la página.
Seleccione un instructor que tenga un curso asignado y, a continuación, seleccione el curso.
Ahora ha visto algunas maneras de trabajar con datos relacionados. En el siguiente tutorial, aprenderá a agregar relaciones entre entidades existentes, a quitar relaciones y a agregar una nueva entidad que tenga una relación con una entidad existente.