Uso de Entity Framework 4.0 y el control ObjectDataSource, parte 3: Ordenar y filtrar

de Tom Dykstra

Esta serie de tutoriales se basa en la aplicación web Contoso University creada por la Introducción a Entity Framework 4.0 serie de tutoriales. Si no completó los tutoriales anteriores, como punto de partida de este tutorial, puede descargar la aplicación que habría creado. También puede descargar la aplicación creada mediante la serie de tutoriales completados. Si tiene preguntas sobre los tutoriales, puede publicarlas en el foro de ASP.NET Entity Framework.

En el tutorial anterior implementó el patrón de repositorio en una aplicación web de n niveles que usa Entity Framework y el ObjectDataSource control. En este tutorial se muestra cómo realizar la ordenación y el filtrado y controlar escenarios de detalles maestros. Agregará las siguientes mejoras a la página de Departments.aspx:

  • Cuadro de texto para permitir que los usuarios seleccionen departamentos por nombre.
  • Lista de cursos para cada departamento que se muestra en la cuadrícula.
  • La capacidad de ordenar haciendo clic en encabezados de columna.

A screenshot that shows the Departments page ready for enhancements.

Adición de la capacidad de ordenar columnas GridView

Abra la página Departments.aspx y agregue un SortParameterName="sortExpression" atributo al ObjectDataSource control denominado DepartmentsObjectDataSource. (Más adelante creará un GetDepartments método que toma un parámetro denominado sortExpression). El marcado de la etiqueta de apertura del control ahora es similar al ejemplo siguiente.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartments" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" SortParameterName="sortExpression" >

Agregue el AllowSorting="true" atributo a la etiqueta de apertura del GridView control. El marcado de la etiqueta de apertura del control ahora es similar al ejemplo siguiente.

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        AllowSorting="true" >

En Departments.aspx.cs, establezca el criterio de ordenación predeterminado llamando al GridView método del Sort control desde el Page_Load método:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        DepartmentsGridView.Sort("Name", SortDirection.Ascending);
    }
}

Puede agregar código que ordene o filtre en la clase lógica de negocios o en la clase de repositorio. Si lo hace en la clase lógica de negocios, el trabajo de ordenación o filtrado se realizará después de recuperar los datos de la base de datos, ya que la clase lógica de negocios funciona con un IEnumerable objeto devuelto por el repositorio. Si agrega código de ordenación y filtrado en la clase de repositorio y lo hace antes de convertir una consulta de objeto o expresión LINQ en un IEnumerable objeto, los comandos se pasarán a la base de datos para su procesamiento, lo que suele ser más eficaz. En este tutorial, implementará la ordenación y el filtrado de una manera que haga que la base de datos realice el procesamiento, es decir, en el repositorio.

Para agregar la funcionalidad de ordenación, debe agregar un nuevo método a la interfaz del repositorio y a las clases de repositorio, así como a la clase lógica de negocios. En el archivo ISchoolRepository.cs, agregue un nuevo GetDepartments método que tome un sortExpression parámetro que se usará para ordenar la lista de departamentos que se devuelven:

IEnumerable<Department> GetDepartments(string sortExpression);

El sortExpression parámetro especificará la columna en la que se va a ordenar y la dirección de ordenación.

Agregue código para el nuevo método al archivo SchoolRepository.cs:

public IEnumerable<Department> GetDepartments(string sortExpression)
{
    if (String.IsNullOrWhiteSpace(sortExpression))
    {
        sortExpression = "Name";
    }
    return context.Departments.Include("Person").OrderBy("it." + sortExpression).ToList();
}

Cambie el método sin GetDepartments parámetros existente para llamar al nuevo método:

public IEnumerable<Department> GetDepartments()
{
    return GetDepartments("");
}

En el proyecto de prueba, agregue el siguiente método nuevo a MockSchoolRepository.cs:

public IEnumerable<Department> GetDepartments(string sortExpression)
{
    return departments;
}

Si va a crear pruebas unitarias que dependan de este método que devuelvan una lista ordenada, deberá ordenar la lista antes de devolverla. No va a crear pruebas como las de este tutorial, por lo que el método solo puede devolver la lista de departamentos no ordenados.

En el archivo SchoolBL.cs, agregue el siguiente método nuevo a la clase lógica de negocios:

public IEnumerable<Department> GetDepartments(string sortExpression)
{
    return schoolRepository.GetDepartments(sortExpression);
}

Este código pasa el parámetro de ordenación al método de repositorio.

Ejecute la página Departments.aspx.

Image02

Ahora puede hacer clic en cualquier encabezado de columna para ordenar por esa columna. Si la columna ya está ordenada, al hacer clic en el encabezado se invierte la dirección de ordenación.

En esta sección agregará un cuadro de texto de búsqueda, lo vinculará al ObjectDataSource control mediante un parámetro de control y agregará un método a la clase lógica de negocios para admitir el filtrado.

Abra la página Departments.aspx y agregue el marcado siguiente entre el encabezado y el primer ObjectDataSource control:

Enter any part of the name or leave the box blank to see all names:
    <asp:TextBox ID="SearchTextBox" runat="server" AutoPostBack="true"></asp:TextBox>
     <asp:Button ID="SearchButton" runat="server" Text="Search" />

En el ObjectDataSource control denominado DepartmentsObjectDataSource, haga lo siguiente:

  • Agregue un SelectParameters elemento para un parámetro denominado nameSearchString que obtiene el valor especificado en el SearchTextBox control.
  • Cambie el SelectMethod valor del atributo a GetDepartmentsByName. (Creará este método más adelante).

El marcado del ObjectDataSource control ahora es similar al ejemplo siguiente:

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" TypeName="ContosoUniversity.BLL.SchoolBL"
        SelectMethod="GetDepartmentsByName" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        DataObjectTypeName="ContosoUniversity.DAL.Department" ConflictDetection="CompareAllValues"
        SortParameterName="sortExpression" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated">
        <SelectParameters>
            <asp:ControlParameter ControlID="SearchTextBox" Name="nameSearchString" PropertyName="Text"
                Type="String" />
        </SelectParameters>
    </asp:ObjectDataSource>

En ISchoolRepository.cs, añada un GetDepartmentsByName método que tome los parámetros sortExpression y nameSearchString:

IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString);

En SchoolRepository.cs, añada el siguiente método nuevo:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    if (String.IsNullOrWhiteSpace(sortExpression))
    {
        sortExpression = "Name";
    }
    if (String.IsNullOrWhiteSpace(nameSearchString))
    {
        nameSearchString = "";
    }
    return context.Departments.Include("Person").OrderBy("it." + sortExpression).Where(d => d.Name.Contains(nameSearchString)).ToList();
}

Este código usa un Where método para seleccionar elementos que contengan la cadena de búsqueda. Si la cadena de búsqueda está vacía, se seleccionarán todos los registros. Tenga en cuenta que cuando se especifican llamadas de método juntas en una instrucción como esta (Include, a continuación OrderBy, a continuación Where), el Where método siempre debe ser el último.

Cambie el método existente GetDepartments que toma un sortExpression parámetro para llamar al nuevo método:

public IEnumerable<Department> GetDepartments(string sortExpression)
{
    return GetDepartmentsByName(sortExpression, "");
}

En MockSchoolRepository.cs del proyecto de prueba, agregue el siguiente método nuevo:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    return departments;
}

En SchoolBL.cs, agregue el siguiente método nuevo:

public IEnumerable<Department> GetDepartmentsByName(string sortExpression, string nameSearchString)
{
    return schoolRepository.GetDepartmentsByName(sortExpression, nameSearchString);
}

Ejecute la página Departments.aspx y escriba una cadena de búsqueda para asegurarse de que la lógica de selección funciona. Deje el cuadro de texto vacío e intente buscar para asegurarse de que se devuelven todos los registros.

Image03

Agregar una columna de detalles para cada fila de cuadrícula

A continuación, quiere ver todos los cursos de cada departamento que se muestran en la celda derecha de la cuadrícula. Para ello, usará un GridView control anidado y lo enlazará a los datos de la Courses propiedad de navegación de la Department entidad.

Abra Departments.aspx y, en el marcado del GridView control, especifique un controlador para el RowDataBound evento. El marcado de la etiqueta de apertura del control ahora es similar al ejemplo siguiente.

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" DataKeyNames="DepartmentID" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        OnRowDataBound="DepartmentsGridView_RowDataBound"
        AllowSorting="True" >

Agregue un nuevo TemplateField elemento después del Administrator campo de plantilla:

<asp:TemplateField HeaderText="Courses">
                <ItemTemplate>
                    <asp:GridView ID="CoursesGridView" runat="server" AutoGenerateColumns="False">
                        <Columns>
                            <asp:BoundField DataField="CourseID" HeaderText="ID" />
                            <asp:BoundField DataField="Title" HeaderText="Title" />
                        </Columns>
                    </asp:GridView>
                </ItemTemplate>
            </asp:TemplateField>

Este marcado crea un control anidado GridView que muestra el número de curso y el título de una lista de cursos. No especifica un origen de datos porque lo enlazará en el código del RowDataBound controlador.

Abra Departments.aspx.cs y agregue el controlador siguiente para el RowDataBound evento:

protected void DepartmentsGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        var department = e.Row.DataItem as Department;
        var coursesGridView = (GridView)e.Row.FindControl("CoursesGridView");
        coursesGridView.DataSource = department.Courses.ToList();
        coursesGridView.DataBind();
    }
}

Este código obtiene la Department entidad de los argumentos de evento, convierte la Courses propiedad de navegación en una List colección y los enlaces de datos anidados GridView a la colección.

Abra el archivo SchoolRepository.cs y especifique la carga diligente de la Courses propiedad de navegación llamando al Include método en la consulta de objeto que se crea en el GetDepartmentsByName método. La return instrucción del GetDepartmentsByName método ahora es similar al ejemplo siguiente.

return context.Departments.Include("Person").Include("Courses").
    OrderBy("it." + sortExpression).Where(d => d.Name.Contains(nameSearchString)).ToList();

Ejecute la página. Además de la funcionalidad de ordenación y filtrado que agregó anteriormente, el control GridView ahora muestra los detalles del curso anidados para cada departamento.

A screenshot that shows the Grid View control displaying nested course details.

Esto completa la introducción a los escenarios de ordenación, filtrado y detalles maestros. En el siguiente tutorial verá cómo tratar la simultaneidad.