Introducción a Entity Framework 4.0 Database First y ASP.NET 4 Web Forms: parte 5
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, consulta el primer tutorial de la serie
Trabajar con datos relacionados, continuación
En el tutorial anterior comenzaste a usar el control EntityDataSource
para trabajar con datos relacionados. Has mostrado varios niveles de jerarquía y datos editados en las propiedades de navegación. En este tutorial seguirás trabajando con datos relacionados agregando y eliminando relaciones y agregando una nueva entidad que tenga una relación con una entidad existente.
Crearás una página que agrega cursos asignados a los departamentos. Los departamentos ya existen y, al crear un nuevo curso, al mismo tiempo se establecerá una relación entre él y un departamento existente.
También crearás una página que funcione con una relación de varios a varios asignando un instructor a un curso (agregando una relación entre dos entidades que selecciones) o quitando un instructor de un curso (quitando una relación entre dos entidades que selecciones). En la base de datos, agregar una relación entre un instructor y un curso da como resultado que se agregue una nueva fila a la tabla de asociación CourseInstructor
; quitar una relación implica eliminar una fila de la tabla de asociación CourseInstructor
. Sin embargo, lo haces en Entity Framework estableciendo propiedades de navegación, sin hacer referencia a la tabla CourseInstructor
explícitamente.
Agregar una entidad con una relación a una entidad existente
Crea una nueva página web denominada CoursesAdd.aspx que use la página maestra Site.Master y agrega el marcado siguiente al control Content
denominado Content2
:
<h2>Add Courses</h2>
<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="Courses"
EnableInsert="True" EnableDelete="True" >
</asp:EntityDataSource>
<asp:DetailsView ID="CoursesDetailsView" runat="server" AutoGenerateRows="False"
DataSourceID="CoursesEntityDataSource" DataKeyNames="CourseID"
DefaultMode="Insert" oniteminserting="CoursesDetailsView_ItemInserting">
<Fields>
<asp:BoundField DataField="CourseID" HeaderText="ID" />
<asp:BoundField DataField="Title" HeaderText="Title" />
<asp:BoundField DataField="Credits" HeaderText="Credits" />
<asp:TemplateField HeaderText="Department">
<InsertItemTemplate>
<asp:EntityDataSource ID="DepartmentsEntityDataSource" runat="server" ConnectionString="name=SchoolEntities"
DefaultContainerName="SchoolEntities" EnableDelete="True" EnableFlattening="False"
EntitySetName="Departments" EntityTypeFilter="Department">
</asp:EntityDataSource>
<asp:DropDownList ID="DepartmentsDropDownList" runat="server" DataSourceID="DepartmentsEntityDataSource"
DataTextField="Name" DataValueField="DepartmentID"
oninit="DepartmentsDropDownList_Init">
</asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
Este marcado crea un control EntityDataSource
que selecciona los cursos, que habilita la inserción y que especifica un controlador para el eventoInserting
. Usarás el controlador para actualizar la propiedad de navegación Department
cuando se cree una nueva entidad Course
.
El marcado también crea un control DetailsView
que se usará para agregar nuevas entidades Course
. El marcado usa campos enlazados para las propiedades Course
de la entidad. Debes escribir el valor CourseID
porque no es un campo de identificador generado por el sistema. En su lugar, es un número de curso que se debe especificar manualmente cuando se crea el curso.
Se usa un campo de plantilla para la propiedad de navegación Department
porque las propiedades de navegación no se pueden usar con controles BoundField
. El campo de plantilla proporciona una lista desplegable para seleccionar el departamento. La lista desplegable está enlazada al conjunto de entidades Departments
mediante Eval
en lugar de Bind
, de nuevo porque no puedes enlazar directamente las propiedades de navegación para actualizarlas. Especifica un controlador para el evento DropDownList
del control Init
para que puedas almacenar una referencia al control para que lo use el código que actualiza la clave externa DepartmentID
.
En CoursesAdd.aspx.cs justo después de la declaración de clase parcial, agrega un campo de clase para contener una referencia al control DepartmentsDropDownList
:
private DropDownList departmentDropDownList;
Agrega un controlador para el evento DepartmentsDropDownList
del control Init
para que puedas almacenar una referencia al control. Esto te permite obtener el valor especificado por el usuario y usarlo para actualizar el valor DepartmentID
de la entidad Course
.
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
departmentDropDownList = sender as DropDownList;
}
Implementa un controlador para el evento Inserting
del control DetailsView
:
protected void CoursesDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
var departmentID = Convert.ToInt32(departmentDropDownList.SelectedValue);
e.Values["DepartmentID"] = departmentID;
}
Cuando el usuario hace clic en Insert
, el evento Inserting
se genera antes de insertar el nuevo registro. El código del controlador obtiene DepartmentID
del control DropDownList
y lo usa para establecer el valor que se usará para la propiedad DepartmentID
de la entidad Course
.
Entity Framework se encargará de agregar este curso a la propiedad de navegación Courses
de la entidad Department
asociada. También agrega el departamento a la propiedad de navegación Department
de la entidad Course
.
Ejecuta la página.
Escribe un identificador, un título, un número de créditos y selecciona un departamento y, a continuación, haz clic en Insertar.
Ejecuta la página Courses.aspx y selecciona el mismo departamento para ver el nuevo curso.
Trabajar con relaciones de varios a varios
La relación entre el conjunto de entidades Courses
y el conjunto de entidades People
es una relación de varios a varios. Una entidad Course
tiene una propiedad de navegación denominada People
que puede contener cero, una o más entidades relacionadas Person
(que representan a los instructores asignados para enseñar ese curso). Y una entidad Person
tiene una propiedad de navegación denominada Courses
que puede contener cero, una o más entidades relacionadas Course
(que representan cursos que se asignan al instructor para enseñar). Un instructor podría enseñar varios cursos y un curso podría ser enseñado por varios instructores. En esta sección del tutorial, agregarás y quitarás las relaciones entre las entidades Person
y Course
mediante la actualización de las propiedades de navegación de las entidades relacionadas.
Crea una nueva página web denominada InstructorsCourses.aspx que use la página maestra Site.Master y agrega el siguiente marcado al control Content
denominado Content2
:
<h2>Assign Instructors to Courses or Remove from Courses</h2>
<br />
<asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="People"
Where="it.HireDate is not null" Select="it.LastName + ', ' + it.FirstMidName AS Name, it.PersonID">
</asp:EntityDataSource>
Select an Instructor:
<asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsEntityDataSource"
AutoPostBack="true" DataTextField="Name" DataValueField="PersonID"
OnSelectedIndexChanged="InstructorsDropDownList_SelectedIndexChanged"
OnDataBound="InstructorsDropDownList_DataBound">
</asp:DropDownList>
<h3>
Assign a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="UnassignedCoursesDropDownList" runat="server"
DataTextField="Title" DataValueField="CourseID">
</asp:DropDownList>
<br />
<asp:Button ID="AssignCourseButton" runat="server" Text="Assign" OnClick="AssignCourseButton_Click" />
<br />
<asp:Label ID="CourseAssignedLabel" runat="server" Visible="false" Text="Assignment successful"></asp:Label>
<br />
<h3>
Remove a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="AssignedCoursesDropDownList" runat="server"
DataTextField="title" DataValueField="courseiD">
</asp:DropDownList>
<br />
<asp:Button ID="RemoveCourseButton" runat="server" Text="Remove" OnClick="RemoveCourseButton_Click" />
<br />
<asp:Label ID="CourseRemovedLabel" runat="server" Visible="false" Text="Removal successful"></asp:Label>
Este marcado crea un control EntityDataSource
que recupera el nombre y PersonID
de las entidades Person
de los instructores. Un control DropDrownList
está enlazado al control EntityDataSource
. El control DropDownList
especifica un controlador para el evento DataBound
. Usarás este controlador para enlazar los dos listas desplegables que muestran los cursos.
El marcado también crea el siguiente grupo de controles que se usarán para asignar un curso al instructor seleccionado:
- Un control
DropDownList
para seleccionar un curso que se va a asignar. Este control se rellenará con cursos que actualmente no están asignados al instructor seleccionado. - Un control
Button
para iniciar la asignación. - Un control
Label
para mostrar un mensaje de error si se produce un error en la asignación.
Por último, el marcado también crea un grupo de controles que se usarán para quitar un curso del instructor seleccionado.
En InstructorsCourses.aspx.cs, agrega una instrucción using:
using ContosoUniversity.DAL;
Agrega un método para rellenar las dos listas desplegables que muestran cursos:
private void PopulateDropDownLists()
{
using (var context = new SchoolEntities())
{
var allCourses = (from c in context.Courses
select c).ToList();
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People.Include("Courses")
where p.PersonID == instructorID
select p).First();
var assignedCourses = instructor.Courses.ToList();
var unassignedCourses = allCourses.Except(assignedCourses.AsEnumerable()).ToList();
UnassignedCoursesDropDownList.DataSource = unassignedCourses;
UnassignedCoursesDropDownList.DataBind();
UnassignedCoursesDropDownList.Visible = true;
AssignedCoursesDropDownList.DataSource = assignedCourses;
AssignedCoursesDropDownList.DataBind();
AssignedCoursesDropDownList.Visible = true;
}
}
Este código obtiene todos los cursos del conjunto de entidades Courses
y obtiene los cursos de la propiedad de navegación Courses
de la entidad Person
para el instructor seleccionado. A continuación, determina qué cursos se asignan a ese instructor y rellena las listas desplegables en consecuencia.
Agrega un controlador para el evento Click
del botón Assign
:
protected void AssignCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(UnassignedCoursesDropDownList.SelectedValue);
var course = (from c in context.Courses
where c.CourseID == courseID
select c).First();
instructor.Courses.Add(course);
try
{
context.SaveChanges();
PopulateDropDownLists();
CourseAssignedLabel.Text = "Assignment successful.";
}
catch (Exception)
{
CourseAssignedLabel.Text = "Assignment unsuccessful.";
//Add code to log the error.
}
CourseAssignedLabel.Visible = true;
}
}
Este código obtiene la entidad Person
del instructor seleccionado, obtiene la entidad Course
del curso seleccionado y agrega el curso seleccionado a la propiedad de navegación Courses
de la entidad Person
del instructor. A continuación, guarda los cambios en la base de datos y vuelve a rellenar las listas desplegables para que los resultados se puedan ver inmediatamente.
Agrega un controlador para el evento Click
del botón Remove
:
protected void RemoveCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(AssignedCoursesDropDownList.SelectedValue);
var courses = instructor.Courses;
var courseToRemove = new Course();
foreach (Course c in courses)
{
if (c.CourseID == courseID)
{
courseToRemove = c;
break;
}
}
try
{
courses.Remove(courseToRemove);
context.SaveChanges();
PopulateDropDownLists();
CourseRemovedLabel.Text = "Removal successful.";
}
catch (Exception)
{
CourseRemovedLabel.Text = "Removal unsuccessful.";
//Add code to log the error.
}
CourseRemovedLabel.Visible = true;
}
}
Este código obtiene la entidad Person
del instructor seleccionado, obtiene la entidad Course
del curso seleccionado y quita el curso seleccionado de la propiedad de navegación Courses
de la entidad Person
. A continuación, guarda los cambios en la base de datos y vuelve a rellenar las listas desplegables para que los resultados se puedan ver inmediatamente.
Agrega código al método Page_Load
que garantiza que los mensajes de error no estén visibles cuando no hay ningún error que notificar y agrega controladores para los eventos DataBound
y SelectedIndexChanged
de la lista desplegable de instructores para rellenar las listas desplegables de cursos:
protected void Page_Load(object sender, EventArgs e)
{
CourseAssignedLabel.Visible = false;
CourseRemovedLabel.Visible = false;
}
protected void InstructorsDropDownList_DataBound(object sender, EventArgs e)
{
PopulateDropDownLists();
}
protected void InstructorsDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
PopulateDropDownLists();
}
Ejecuta la página.
Selecciona un instructor. La lista desplegable Asignar un curso muestra los cursos a los que el instructor no enseña y la lista desplegable Quitar un curso muestra los cursos a los que el instructor ya está asignado. En la sección Asignar un curso, selecciona un curso y, a continuación, haz clic en Asignar. El curso se mueve a la lista desplegable Quitar un curso. Selecciona un curso en la sección Quitar un curso y haz clic en Quitar. El curso se mueve a la lista desplegable Asignar un curso.
Ahora has visto algunas formas más de trabajar con datos relacionados. En el siguiente tutorial, aprenderás a usar la herencia en el modelo de datos para mejorar la capacidad de mantenimiento de la aplicación.