Entity Framework 4.0 と ObjectDataSource コントロールの使用、パート 3: 並べ替えとフィルター処理

著者: Tom Dykstra

このチュートリアル シリーズは、Entity Framework 4.0 の概要チュートリアル シリーズで作成された Contoso University Web アプリケーションをベースとしています。 前のチュートリアルを終わらせていない場合は、このチュートリアルの始めに、前のチュートリアルで作成するはずであったアプリケーションをダウンロードできます。 完了したチュートリアル シリーズで作成されたアプリケーションをダウンロードすることもできます。 チュートリアルに関する質問がある場合は、ASP.NET Entity Framework フォーラムに質問を投稿できます。

前のチュートリアルでは、Entity Framework と ObjectDataSource コントロールを使用する n 層 Web アプリケーションにリポジトリ パターンを実装しました。 このチュートリアルでは、並べ替えとフィルター処理を行い、マスター詳細シナリオを処理する方法について説明します。 Departments.aspx ページに次の機能強化を追加します。

  • ユーザーが名前で部門を選択できるテキスト ボックス。
  • グリッドに表示される各部門向けのコースのリスト。
  • 列見出しをクリックして並べ替える機能。

A screenshot that shows the Departments page ready for enhancements.

GridView 列を並べ替える機能の追加

Departments.aspx ページを開き、DepartmentsObjectDataSource という名前の ObjectDataSource コントロールに SortParameterName="sortExpression" 属性を追加します。 (後で、sortExpression というパラメーターを受け取る GetDepartments メソッドを作成します。)コントロールの開始タグのマークアップは、次の例のようになります。

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

GridView コントロールの開始タグに AllowSorting="true" 属性を追加します。 コントロールの開始タグのマークアップは、次の例のようになります。

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

このコードは、並べ替えパラメーターをリポジトリ メソッドに渡します。

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

DepartmentsObjectDataSource という名前の ObjectDataSource コントロールで、次の操作を行います。

  • SearchTextBox コントロールに入力された値を取得する nameSearchString という名前のパラメーターの SelectParameters 要素を追加します。
  • 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.csGetDepartmentsByName メソッドを追加します。これは、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 メソッドを使用して、検索文字列を含む項目を選択します。 検索文字列が空の場合は、すべてのレコードが選択されます。 1 つのステートメントで複数のメソッド呼び出しを一緒に指定する場合 (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 コントロールを使用し、Department エンティティの Courses ナビゲーション プロパティのデータにデータバインドします。

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 ファイルを開き、GetDepartmentsByName メソッドで作成したオブジェクト クエリで Include メソッドを呼び出して、Courses ナビゲーション プロパティの一括読み込みを指定します。 GetDepartmentsByName メソッドの return ステートメントは、次の例のようになります。

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

このページを実行します。 前に追加した並べ替えとフィルター処理の機能に加えて、GridView コントロールに各部門向けの入れ子になったコースの詳細が表示されるようになりました。

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

これで、並べ替え、フィルター処理、マスター詳細シナリオの概要が完了しました。 次のチュートリアルでは、コンカレンシーを処理する方法を説明します。