使用 Entity Framework 4.0 和 ObjectDataSource 控件,第 3 部分:排序和筛选

作者 :Tom Dykstra

本教程系列基于由入门使用 Entity Framework 4.0 教程系列创建的 Contoso University Web 应用程序。 如果未完成前面的教程,作为本教程的起点,可以下载已创建 的应用程序 。 还可以下载完整教程系列创建 的应用程序 。 如果对教程有疑问,可将其发布到 ASP.NET 实体框架论坛

在上一教程中,你在使用实体框架和 ObjectDataSource 控件的 n 层 Web 应用程序中实现了存储库模式。 本教程演示如何进行排序和筛选以及处理主-详细信息方案。 你将向 Departments.aspx 页面添加以下增强功能:

  • 允许用户按名称选择部门的文本框。
  • 网格中显示的每个部门的课程列表。
  • 通过单击列标题进行排序的功能。

显示“部门”页的屏幕截图,该页面已准备好进行增强。

添加对 GridView 列进行排序的功能

打开 Departments.aspx 页,并将属性SortParameterName="sortExpression"添加到名为 的ObjectDataSourceDepartmentsObjectDataSource控件。 (稍后将创建一个 GetDepartments 方法,该方法采用名为 sortExpression.) 控件的开始标记的标记现在类似于以下示例。

<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" >

AllowSorting="true" 属性添加到 控件的 GridView 开始标记。 控件的开始标记的标记现在类似于以下示例。

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

Departments.aspx.cs 中,通过从 Page_Load 方法调用 GridView 控件的 Sort 方法设置默认排序顺序:

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

可以在业务逻辑类或存储库类中添加排序或筛选的代码。 如果在业务逻辑类中执行此操作,则排序或筛选工作将在从数据库中检索数据后完成,因为业务逻辑类使用 IEnumerable 存储库返回的对象。 如果在存储库类中添加排序和筛选代码,并在 LINQ 表达式或对象查询转换为 IEnumerable 对象之前执行此操作,则命令将传递到数据库进行处理,这通常效率更高。 在本教程中,你将实现排序和筛选,使处理由数据库(即在存储库中)完成。

若要添加排序功能,必须将新方法添加到存储库接口和存储库类以及业务逻辑类。 在 ISchoolRepository.cs 文件中,添加采用参数的新 GetDepartments 方法 sortExpression ,该参数将用于对返回的部门列表进行排序:

IEnumerable<Department> GetDepartments(string sortExpression);

参数 sortExpression 将指定要排序的列和排序方向。

将新方法的代码添加到 SchoolRepository.cs 文件:

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

更改现有无 GetDepartments 参数方法以调用新方法:

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

在测试项目中,将以下新方法添加到 MockSchoolRepository.cs

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

如果要创建依赖于此方法返回排序列表的任何单元测试,则需要先对列表进行排序,然后再返回它。 在本教程中,你不会创建此类测试,因此该方法只能返回未排序的部门列表。

SchoolBL.cs 文件中,将以下新方法添加到业务逻辑类:

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

此代码将 sort 参数传递给存储库方法。

运行 Departments.aspx 页。

Image02

现在可以单击任何列标题,按该列进行排序。 如果列已排序,则单击标题将反转排序方向。

在本部分中,你将添加一个搜索文本框,使用控件参数将其 ObjectDataSource 链接到控件,并将方法添加到业务逻辑类以支持筛选。

打开 Departments.aspx 页,并在标题和第一个 ObjectDataSource 控件之间添加以下标记:

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" />

在名为 的ObjectDataSourceDepartmentsObjectDataSource控件中,执行以下操作:

  • 为名为 nameSearchString 的参数添加一个SelectParameters元素,用于获取在 控件中SearchTextBox输入的值。
  • SelectMethod 属性值更改为 GetDepartmentsByName。 (稍后将创建此方法。)

控件的 ObjectDataSource 标记现在类似于以下示例:

<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>

ISchoolRepository.cs 中,添加一个 GetDepartmentsByName 同时采用 sortExpressionnameSearchString 参数的方法:

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

SchoolRepository.cs 中,添加以下新方法:

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();
}

此代码使用 方法 Where 选择包含搜索字符串的项。 如果搜索字符串为空,则将选择所有记录。 请注意,在一个语句中一起指定方法调用时,如 (Include,然后 OrderBy,然后 Where) , Where 该方法必须始终是最后一个。

更改采用sortExpression参数来调用新方法的现有GetDepartments方法:

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

在测试项目的 MockSchoolRepository.cs 中,添加以下新方法:

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

SchoolBL.cs 中,添加以下新方法:

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

运行 Departments.aspx 页并输入搜索字符串以确保选择逻辑正常工作。 将文本框留空,然后尝试搜索以确保返回所有记录。

Image03

为每个网格行添加详细信息列

接下来,你希望在网格的右侧单元格中查看每个部门的所有课程。 为此,你将使用嵌套GridView控件,并将其数据绑定到实体导航属性中的数据CoursesDepartment

打开 Departments.aspx ,并在控件的标记中 GridView 指定 事件的处理程序 RowDataBound 。 控件的开始标记的标记现在类似于以下示例。

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

在模板字段后Administrator添加新TemplateField元素:

<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>

此标记创建一个嵌套 GridView 控件,用于显示课程列表的课程编号和标题。 它不指定数据源,因为你将在处理程序中的代码中 RowDataBound 将其数据绑定。

打开 Departments.aspx.cs 并为 事件添加以下处理程序 RowDataBound

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();
    }
}

此代码从事件参数中获取 Department 实体,将 Courses 导航属性转换为 List 集合,并将嵌套 GridView 的 数据绑定到集合。

打开 SchoolRepository.cs 文件,通过在 方法中创建的对象查询中调用 Include 方法来指定导航属性的GetDepartmentsByName预先加载Courses。 方法 return 中的 GetDepartmentsByName 语句现在类似于以下示例。

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

运行页面。 除了前面添加的排序和筛选功能外,GridView 控件现在还显示每个部门的嵌套课程详细信息。

显示显示嵌套课程详细信息的网格视图控件的屏幕截图。

这将完成排序、筛选和大纲-详细信息方案的介绍。 在下一教程中,你将了解如何处理并发。