Introducción a Entity Framework 4.0 Database First y ASP.NET 4 Web Forms: parte 2

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.

El control EntityDataSource

En el tutorial anterior creó un sitio web, una base de datos y un modelo de datos. En este tutorial se trabaja con el control EntityDataSource que proporciona ASP.NET para facilitar el trabajo con un modelo de datos de Entity Framework. Creará un control GridView para mostrar y editar datos de alumnos, un control DetailsView para agregar nuevos alumnos y un control DropDownList para seleccionar un departamento (que usará más adelante para mostrar cursos asociados).

Screenshot of the Internet Explorer window, which is showing the Student List view with a list of students' names, enrollment dates, and courses.

Screenshot of the Internet Explorer window, showing the Add New Students view with John Smith's name and enrollment date filled into the text fields.

Screenshot of the Internet Explorer window, which is showing the Courses by Department view with a dropdown menu for departments.

Tenga en cuenta que en esta aplicación no agregará ninguna validación de entrada a páginas que actualicen la base de datos y algunos de los controles de errores no serán tan sólidos como sería necesario en una aplicación de producción. Esto mantiene el tutorial centrado en Entity Framework y evita que sea demasiado largo. Para obtener más información sobre cómo agregar estas características a la aplicación, consulte Validación de la entrada de usuario en ASP.NET Web Pages y Control de errores en aplicaciones y ASP.NET Web Pages.

Cómo agregar y configurar el control EntityDataSource

Comenzará configurando un control EntityDataSource para leer entidades Person del conjunto de entidades People.

Asegúrese de que tiene Visual Studio abierto y de que está trabajando con el proyecto que creó en la parte 1. Si no ha compilado el proyecto desde que creó el modelo de datos o desde el último cambio realizado en él, compile el proyecto ahora. Los cambios en el modelo de datos no están disponibles para el diseñador hasta que se compila el proyecto.

Cree una página web con la plantilla Formulario web con página maestra y asígnele el nombre Students.aspx.

Image23

Especifique Site.Master como página maestra. Todas las páginas que cree para estos tutoriales usarán esta página maestra.

Image24

En la vista Origen, agregue un encabezado h2 al control Content denominado Content2, como se muestra en el ejemplo siguiente:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
   <h2>Student List</h2>
</asp:Content>

En la pestaña Datos del cuadro de herramientas, arrastre un control EntityDataSource a la página, colóquelo debajo del encabezado y cambie el identificador a StudentsEntityDataSource:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Student List</h2>
    <asp:EntityDataSource ID="StudentsEntityDataSource" runat="server">
    </asp:EntityDataSource>
</asp:Content>

Cambie a la vista Diseño, haga clic en la etiqueta inteligente del control de origen de datos y, a continuación, haga clic en Configurar origen de datos para iniciar el Asistente para configurar orígenes de datos.

Image01

En el paso Configurar ObjectContext del asistente, seleccione SchoolEntities como valor de Conexión con nombre y seleccione SchoolEntities como valor DefaultContainerName. A continuación, haga clic en Siguiente.

Image02

Nota: Si obtiene el siguiente cuadro de diálogo en este punto, debe compilar el proyecto antes de continuar.

Image25

En el paso Configurar selección de datos, seleccione Personas como valor para EntitySetName. En Seleccionar, asegúrese de que la casilla Seleccionar todo esté activada. A continuación, seleccione las opciones para habilitar la actualización y la eliminación. Cuando haya terminado, haga clic en Finalizar.

Image03

Configuración de reglas de base de datos para permitir la eliminación

Va a crear una página que permita a los usuarios eliminar alumnos de la tabla Person, que tiene tres relaciones con otras tablas (Course, StudentGrade y OfficeAssignment). De forma predeterminada, la base de datos le impedirá eliminar una fila de Person si hay filas relacionadas en una de las otras tablas. Puede eliminar manualmente las filas relacionadas primero o configurar la base de datos para eliminarlas automáticamente al eliminar una fila Person. En el caso de los registros de alumnos de este tutorial, configurará la base de datos para eliminar automáticamente los datos relacionados. Dado que los alumnos solo pueden tener filas relacionadas en la tabla StudentGrade, debe configurar solo una de las tres relaciones.

Si usa el archivo School.mdf que descargó del proyecto que va con este tutorial, puede omitir esta sección porque estos cambios de configuración ya se han realizado. Si creó la base de datos mediante la ejecución de un script, configure la base de datos realizando los procedimientos siguientes.

En el Explorador de servidores, abra el diagrama de base de datos que creó en la parte 1. Haga clic con el botón derecho en la relación entre Person y StudentGrade (la línea entre tablas) y, a continuación, seleccione Propiedades.

Image04

En la ventana Propiedades, expanda Especificación de INSERT y UPDATE y establezca la propiedad DeleteRule en Cascada.

Image05

Guarde y cierre el diagrama. Si se le pregunta si desea actualizar la base de datos, haga clic en .

Para asegurarse de que el modelo mantiene las entidades que están en memoria sincronizadas con lo que está haciendo la base de datos, debe establecer las reglas correspondientes en el modelo de datos. Abra SchoolModel.edmx, haga clic con el botón derecho en la línea de asociación entre Person y StudentGrade y, a continuación, seleccione Propiedades.

Image21

En la ventana Propiedades, establezca End1 OnDelete en Cascada.

Image22

Guarde y cierre el archivo SchoolModel.edmx y vuelva a compilar el proyecto.

En general, cuando cambia la base de datos, tiene varias opciones para sincronizar el modelo:

  • Para determinados tipos de cambios (como agregar o actualizar tablas, vistas o procedimientos almacenados), haga clic con el botón derecho en el diseñador y seleccione Actualizar modelo de base de datos para que el diseñador realice los cambios automáticamente.
  • Vuelva a generar el modelo de datos.
  • Realice actualizaciones manuales como esta.

En este caso, podría haber regenerado el modelo o actualizado las tablas afectadas por el cambio de relación, pero tendría que volver a cambiar el nombre de campo (de FirstName a FirstMidName).

Uso de un control GridView para leer y actualizar entidades

En esta sección, usará un control GridView para mostrar, actualizar o eliminar alumnos.

Abra o cambie a Students.aspx y cambie a la vista Diseño. En la pestaña Datos del cuadro de herramientas, arrastre un control GridView a la derecha del control EntityDataSource, asígnele el nombre StudentsGridView, haga clic en la etiqueta inteligente y, a continuación, seleccione StudentsEntityDataSource como origen de datos.

Image06

Haga clic en Actualizar esquema (haga clic en si se le pide que confirme) y, a continuación, haga clic en Habilitar paginación, Habilitar ordenación, Habilitar edición y Habilitar eliminación.

Haga clic en Editar columnas.

Image10

En el cuadro Campos seleccionados, elimine PersonID, LastName y HireDate. Normalmente no muestra una clave de registro para los usuarios, la fecha de contratación no es relevante para los alumnos y colocará ambas partes del nombre en un campo, por lo que solo necesita uno de los campos de nombre.

Image11

Seleccione el campo FirstMidName y, a continuación, haga clic en Convertir este campo en un campo TemplateField.

Haga lo mismo para EnrollmentDate.

Image13

Haga clic en Aceptar y, a continuación, cambie a la vista Origen. Los cambios restantes serán más fáciles de realizar directamente en el marcado. El marcado del control GridView ahora es similar al ejemplo siguiente.

<asp:GridView ID="StudentsGridView" runat="server" AllowPaging="True" 
        AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="PersonID" 
        DataSourceID="StudentsEntityDataSource">
        <Columns>
            <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
            <asp:TemplateField HeaderText="FirstMidName" SortExpression="FirstMidName">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:TextBox>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="EnrollmentDate" SortExpression="EnrollmentDate">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("EnrollmentDate") %>'></asp:TextBox>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label2" runat="server" Text='<%# Bind("EnrollmentDate") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>

La primera columna después del campo de comando es un campo de plantilla que muestra actualmente el nombre. Cambie el marcado de este campo de plantilla para que tenga un aspecto similar al del ejemplo siguiente:

<asp:TemplateField HeaderText="Name" SortExpression="LastName">
                <EditItemTemplate>
                    <asp:TextBox ID="LastNameTextBox" runat="server" Text='<%# Bind("LastName") %>'></asp:TextBox>
                    <asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:TextBox>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="LastNameLabel" runat="server" Text='<%# Eval("LastName") %>'></asp:Label>,
                    <asp:Label ID="FirstNameLabel" runat="server" Text='<%# Eval("FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>

En el modo de presentación, dos controles Label muestran el nombre y el apellido. En el modo de edición, se proporcionan dos cuadros de texto para que pueda cambiar el nombre y el apellido. Al igual que con los controles Label en modo de presentación, se usan expresiones Bind y Eval exactamente como lo haría con los controles de origen de datos de ASP.NET que se conectan directamente a bases de datos. La única diferencia es que va a especificar propiedades de entidad en lugar de columnas de base de datos.

La última columna es un campo de plantilla que muestra la fecha de inscripción. Cambie el marcado de este campo para que tenga un aspecto similar al del ejemplo siguiente:

<asp:TemplateField HeaderText="Enrollment Date" SortExpression="EnrollmentDate">
                <EditItemTemplate>
                    <asp:TextBox ID="EnrollmentDateTextBox" runat="server" Text='<%# Bind("EnrollmentDate", "{0:d}") %>'></asp:TextBox>
                </EditItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="EnrollmentDateLabel" runat="server" Text='<%# Eval("EnrollmentDate", "{0:d}") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>

En el modo de presentación y edición, la cadena de formato "{0,d}" hace que la fecha se muestre con el formato "fecha corta" (es posible que su equipo esté configurado para mostrar este formato de forma diferente a las imágenes de pantalla que se muestran en este tutorial).

Observe que en cada uno de estos campos de plantilla, el diseñador usó una expresión Bind de forma predeterminada, pero lo ha cambiado a una expresión Eval en los elementos ItemTemplate. La expresión Bind hace que los datos estén disponibles en las propiedades del control GridView en caso de que necesite acceder a los datos del código. En esta página no es necesario acceder a estos datos del código, por lo que puede usar Eval, que es más eficaz. Para obtener más información, consulte Obtención de datos de los controles de datos.

Revisión del marcado del control EntityDataSource para mejorar el rendimiento

En el marcado del control EntityDataSource, quite los atributos ConnectionString y DefaultContainerName y reemplácelos por un atributo ContextTypeName="ContosoUniversity.DAL.SchoolEntities". Este es un cambio que debe realizar cada vez que cree un control EntityDataSource, a menos que necesite usar una conexión diferente de la que está codificada de forma rígida en la clase de contexto de objeto. El uso del atributo ContextTypeName proporciona las siguientes ventajas:

  • Mejor rendimiento. Cuando el control EntityDataSource inicializa el modelo de datos mediante los atributos ConnectionString y DefaultContainerName, realiza un trabajo adicional para cargar metadatos en cada solicitud. Esto no es necesario si especifica el atributo ContextTypeName.
  • La carga diferida se activa de forma predeterminada en las clases de contexto de objetos generadas (como SchoolEntities en este tutorial) en Entity Framework 4.0. Esto significa que las propiedades de navegación se cargan con datos relacionados automáticamente cuando lo necesite. La carga diferida se explica con mayor detalle más adelante en este tutorial.
  • Las personalizaciones que haya aplicado a la clase de contexto de objeto (en este caso, a la clase SchoolEntities) estarán disponibles para los controles que usan el control EntityDataSource. Personalizar la clase de contexto de objeto es un tema avanzado que no se trata en esta serie de tutoriales. Para obtener más información, consulte Extensión de tipos generados por Entity Framework.

El marcado será ahora similar al ejemplo siguiente (el orden de las propiedades puede ser diferente):

<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server"
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
        EntitySetName="People"
        EnableDelete="True" EnableUpdate="True">
    </asp:EntityDataSource>

El atributo EnableFlattening hace referencia a una característica necesaria en versiones anteriores de Entity Framework porque las columnas de clave externa no se expusieron como propiedades de entidad. La versión actual permite usar asociaciones de clave externa, lo que significa que las propiedades de clave externa se exponen para todas las asociaciones de varios a varios. Si las entidades tienen propiedades de clave externa y ningún tipo complejo, puede dejar este atributo establecido en False. No quite el atributo del marcado, porque el valor predeterminado es True. Para obtener más información, consulte Objetos de aplanamiento (EntityDataSource).

Ejecute la página y verá una lista de alumnos y empleados (filtrará solo a los alumnos en el siguiente tutorial). El nombre y apellidos se muestran juntos.

Image07

Para ordenar la presentación, haga clic en un nombre de columna.

Haga clic en Editar en cualquier fila. Los cuadros de texto se muestran donde puede cambiar el nombre y el apellido.

Image08

El botón Eliminar también funciona. Haga clic en Eliminar para una fila que tenga una fecha de inscripción y la fila desaparecerá (las filas sin fecha de inscripción representan a profesores y puede obtener un error de integridad referencial; en el siguiente tutorial, filtrará esta lista para incluir solo a los alumnos).

Mostrar datos desde una propiedad de navegación

Ahora supongamos que quiere saber en cuántos cursos está inscrito cada alumno. Entity Framework proporciona esa información en la propiedad de navegación StudentGrades de la entidad Person. Dado que el diseño de la base de datos no permite que un alumno se inscriba en un curso sin tener asignada una calificación, para este tutorial puede suponer que tener una fila en la fila de tabla StudentGrade asociada a un curso es lo mismo que estar inscrito en el curso (la propiedad de navegación Courses solo es para los profesores).

Cuando se usa el atributo ContextTypeName del control EntityDataSource, Entity Framework recupera automáticamente información de una propiedad de navegación cuando se accede a esa propiedad. Esto se denomina carga diferida. Sin embargo, esto puede ser ineficaz, ya que da como resultado una llamada independiente a la base de datos cada vez que se necesita información adicional. Si necesita datos de la propiedad de navegación para cada entidad devuelta por el control EntityDataSource, es más eficaz recuperar los datos relacionados junto con la propia entidad en una sola llamada a la base de datos. Esto se denomina carga diligente, y se especifica para una propiedad de navegación estableciendo la propiedad Include del control EntityDataSource.

En Students.aspx, quiere mostrar el número de cursos para cada estudiante, por lo que la carga diligente es la mejor opción. Si tuviera que mostrar a todos los alumnos, pero solo el número de cursos de algunos de ellos (lo que requeriría escribir algo de código además del marcado), la carga diferida podría ser una mejor opción.

Abra o cambie a Students.aspx, cambie a la vista Diseño, seleccione StudentsEntityDataSource y, en la ventana Propiedades, establezca la propiedad Include en StudentGrades (si desea obtener varias propiedades de navegación, puede especificar sus nombres separados por comas; por ejemplo, StudentGrades, Courses).

Image19

Cambie a la vista Origen. En el control StudentsGridView, después del último elemento asp:TemplateField, agregue el siguiente campo de plantilla nuevo:

<asp:TemplateField HeaderText="Number of Courses">
                <ItemTemplate>
                    <asp:Label ID="Label1" runat="server" Text='<%# Eval("StudentGrades.Count") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>

En la expresión Eval, puede hacer referencia a la propiedad de navegación StudentGrades. Dado que esta propiedad contiene una colección, tiene una propiedad Count que se puede usar para mostrar el número de cursos en los que está inscrito el alumno. En un tutorial posterior, verá cómo mostrar datos de propiedades de navegación que contienen entidades únicas en lugar de colecciones (tenga en cuenta que no puede usar elementos BoundField para mostrar datos de las propiedades de navegación).

Ejecute la página y verá en cuántos cursos está inscrito cada alumno.

Screenshot of the Internet Explorer window, showing the Add New Students view with John Smith's name and enrollment date filled into the text fields.

Uso de un control DetailsView para insertar entidades

El siguiente paso consiste en crear una página que tenga un control DetailsView que le permitirá agregar nuevos alumnos. Cierre el explorador y cree una página web con la página maestra Site.Master. Asigne un nombre a la página StudentsAdd.aspx y, a continuación, cambie a la vista Origen.

Agregue el marcado siguiente para reemplazar el marcado existente para el control Content denominado Content2:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Add New Students</h2>
    <asp:EntityDataSource ID="StudentsEntityDataSource" runat="server"
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
        EnableInsert="True" EntitySetName="People">
    </asp:EntityDataSource>
    <asp:DetailsView ID="StudentsDetailsView" runat="server" 
        DataSourceID="StudentsEntityDataSource" AutoGenerateRows="False"
        DefaultMode="Insert">
        <Fields>
            <asp:BoundField DataField="FirstMidName" HeaderText="First Name" 
                SortExpression="FirstMidName" />
            <asp:BoundField DataField="LastName" HeaderText="Last Name" 
                SortExpression="LastName" />
            <asp:BoundField DataField="EnrollmentDate" HeaderText="Enrollment Date" 
                SortExpression="EnrollmentDate" />
             <asp:CommandField ShowInsertButton="True" />
       </Fields>
    </asp:DetailsView>
</asp:Content>

Este marcado crea un control EntityDataSource similar al que creó en Students.aspx, salvo que habilita la inserción. Al igual que con el control GridView, los campos enlazados del control DetailsView se codifican exactamente igual que lo harían para un control de datos que se conecta directamente a una base de datos, excepto que hacen referencia a propiedades de entidad. En este caso, el control DetailsView solo se usa para insertar filas, por lo que ha establecido el modo predeterminado en Insert.

Ejecute la página y agregue un nuevo alumno.

Screenshot of the Internet Explorer window, showing the Add New Students view with John Smith's name and enrollment date filled into the text fields.

No pasará nada después de insertar un nuevo alumno, pero si ahora ejecuta Students.aspx, verá la información del nuevo alumno.

Mostrar datos en una lista desplegable

En los pasos siguientes, enlazará un control DropDownList a un conjunto de entidades mediante un control EntityDataSource. En esta parte del tutorial no hará gran cosa con esta lista. Sin embargo, en las partes posteriores, usará la lista para permitir que los usuarios seleccionen un departamento para mostrar los cursos asociados a este.

Cree una nueva página web denominada Courses.aspx. En la vista Origen, agregue un encabezado al control Content denominado Content2:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Courses by Department</h2>
</asp:Content>

En la vista Diseño, agregue un control EntityDataSource a la página como hizo antes, excepto que esta vez debe asignarle el nombre DepartmentsEntityDataSource. Seleccione Departamentos como valor EntitySetName y seleccione solo las propiedades DepartmentID y Name.

Image15

En la pestaña Estándar del cuadro de herramientas, arrastre un control DropDownList a la página, asígnele el nombre DepartmentsDropDownList, haga clic en la etiqueta inteligente y seleccione Elegir origen de datos para iniciar el Asistente para configuración de orígenes de datos.

Image16

En el paso Elija un origen de datos, seleccione DepartmentsEntityDataSource como origen de datos, haga clic en Actualizar esquema y, a continuación, seleccione Nombre como campo de datos para mostrar y DepartmentID como campo de datos de valor. Haga clic en OK.

Image17

El método que se usa para vincular el control mediante Entity Framework es el mismo que con otros controles de origen de datos de ASP.NET, excepto si se especifican entidades y propiedades de entidad.

Cambie a la vista Origen y agregue "Seleccionar un departamento:" inmediatamente antes del control DropDownList.

Select a department:
    <asp:DropDownList ID="DropDownList1" runat="server" 
        DataSourceID="EntityDataSource1" DataTextField="Name" 
        DataValueField="DepartmentID">
    </asp:DropDownList>

Como recordatorio, cambie el marcado del control EntityDataSource en este momento reemplazando los atributos ConnectionString y DefaultContainerName por un atributo ContextTypeName="ContosoUniversity.DAL.SchoolEntities". A menudo es mejor esperar a que haya creado el control enlazado a datos que esté vinculado al control de origen de datos antes de cambiar el marcado del control EntityDataSource, ya que después de hacer el cambio, el diseñador no le proporcionará ninguna opción Actualizar esquema en el control enlazado a datos.

Ejecute la página y podrá seleccionar un departamento en la lista desplegable.

Screenshot of the Internet Explorer browser window, which shows the Courses by Department view with a dropdown menu for departments.

Esto completa la introducción al uso del control EntityDataSource. Trabajar con este control no suele ser diferente de trabajar con otros controles de origen de datos de ASP.NET, salvo que se hace referencia a entidades y propiedades en lugar de a tablas y columnas. La única excepción es cuando se desea acceder a las propiedades de navegación. En el siguiente tutorial verá que la sintaxis que se usa con el control EntityDataSource también puede diferir de otros controles de origen de datos al filtrar, agrupar y ordenar datos.