SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ASP.NET Dynamic Data活用編

ASP.NET Dynamic Dataのエンティティテンプレートのカスタマイズと勘所

ASP.NET Dynamic Data活用編(4)

  • X ポスト
  • このエントリーをはてなブックマークに追加

フィルター作成

 自作のフィルター機能を実装する前にざっくりと全体を紹介します。/DynamicData/Filters/フォルダ内にフィルター機能のユーザーコントロールを作成し、その中でLINQでいうWhereメソッドを自作すると言う流れになります。手順は以下のとおりです。

1.Filtersフォルダ内にユーザーコントロールを追加する

 追加したファイルの名前はDateと設定する。

2.Date.ascxにサーバーコントロールを設定する

 以下のようにサーバーコントロールを設定します。

Date.ascxファイルの修正
<asp:TextBox ID="TextBox1" runat="server" CssClass="DDTextBox" Columns="12"></asp:TextBox>

<asp:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" 
    Enabled="True" TargetControlID="TextBox1">
</asp:CalendarExtender>

<asp:DropDownList runat="server" ID="DropDownList1" CssClass="DDFilter" AutoPostBack="True" 
    onselectedindexchanged="DropDownList1_SelectedIndexChanged" >
    <asp:ListItem Text="同一日" value="==" />
    <asp:ListItem Text="以前" value="<" />
    <asp:ListItem Text="以後" value=">" />
</asp:DropDownList>

 実行時にこのユーザーコントロールは、テキストボックスにフォーカスがある場合にカレンダーがポップアップし、選択入力できます。その後、ドロップダウンリストで選択することで、入力日当日もしくは以前か以後のデータをフィルターで抽出できます。

3.メタデータクラスを作成し、FilterUIHint属性を付加する

 データフィールドをデータと関連付けるためにはメタデータとFilterUIHint属性を使用します。メタデータEDMのパーシャルクラスを作成し、属性クラスを指定します。メタデータにFilterUIHint属性を使用して指定されたデータフィールドは、実行時に指定したデータのカラム名のラベルを自動表示し、指定されたユーザーコントロールをレンダリングします。コードは以下のようになります。

Date.ascxファイルの修正
using System;
using System.ComponentModel.DataAnnotations;

namespace CustomizeDD4
{
    [MetadataType(typeof(Orders.MetaOrders))]
    partial class Orders
    {
        public class MetaOrders
        {
            [FilterUIHint("Date")]
            public DateTime OrderDate;

            [FilterUIHint("Date")]
            public DateTime RequiredDate;

            [FilterUIHint("Date")]
            public DateTime ShippedDate;
        }
    }
}

 今回DateTime型のカラムは、上記通り3つあるので、それぞれにFilterUIHint属性を指定します。これで、Orderテーブルのリストを表示する際にフィルターコントロールが表示されるようになりました。続いて、ユーザーコントロールのコードビハインド側の記載を変更します。

4.Date.ascx.csの継承元を変更させる

 以下のようにDate.ascx.csファイルの記載を変更します。

Date.ascxファイルの修正
public partial class DateFilter : System.Web.DynamicData.QueryableFilterUserControl
    {
        protected DateFilter()
        {
            
        }
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        public override Control FilterControl
        {
            get { return this.TextBox1; }
        }
       
        public override IQueryable GetQueryable(IQueryable source)
        {

        }
    }

 フィルターを作成する際はQueryableFilterUserControl抽象クラスを継承します。この時、GetQueryableメソッドをオーバーライドで実装します。また、フィルターコントロール自身をテキストボックスにオーバーライドして設定します。続いて実際のフィルター処理の実装です。

5.GetQueryableメソッドの実装

 GetQueryableメソッドは、フィルターにより受け取ったクエリを実行するメソッドです。パラメータは使用するデータソースコントロールで、戻り値もIQueryableになります。つまり、GetQueryableメソッドは、LINQならばWhereを実装する場所と捉えて貰えれば間違いありません。

 今回は日付フィルターと言っても、Ordersテーブル上で使用することを目的として作成します。ユーザーコントロールは、FilterUIHint属性で指定した分だけ表示され、処理も実施されます。

 コードは以下のように記載します。

Date.ascxファイルの修正
// ①
public override IQueryable GetQueryable(IQueryable source)
{
    // Nullチェック
    if (string.IsNullOrEmpty(this.TextBox1.Text))
    {
        return source;
    }

    // 今回はOrdersテーブル上で使用をするので非ジェネリックの値をパラメータにメソッドを呼び出す
    if (source.ElementType == typeof(Orders))
        return GetQueryableOfDate(source.OfType<Orders>());

    throw new NotSupportedException();
}

// ②
private IQueryable GetQueryableOfDate(IQueryable<Orders> source)
{
    // ユーザーコントロールから入力日付を取得
    DateTime inputdate = DateTime.Parse(this.TextBox1.Text);

    // ユーザーコントロールから列名を取得して処理を分岐
    switch(this.Column.Name)
    {
        case "OrderDate":
            // 各列の値と入力値による比較を実施後のラムダ式の値を返す
            switch (this.DropDownList1.SelectedValue)
            {
                case "==":
                    return source.Where(x => x.OrderDate == inputdate);
                case ">":
                    return source.Where(x => x.OrderDate > inputdate);
                case "<":
                    return source.Where(x => x.OrderDate < inputdate);
                default:
                    throw new NotSupportedException();
            }
        case "RequiredDate":
            switch (this.DropDownList1.SelectedValue)
            {
                case "==":
                    return source.Where(x => x.RequiredDate == inputdate);
                case ">":
                    return source.Where(x => x.RequiredDate > inputdate);
                case "<":
                    return source.Where(x => x.RequiredDate < inputdate);
                default:
                    throw new NotSupportedException();
            }
        case "ShippedDate":
            switch (this.DropDownList1.SelectedValue)
            {
                case "==":
                    return source.Where(x => x.ShippedDate == inputdate);
                case ">":
                    return source.Where(x => x.ShippedDate > inputdate);
                case "<":
                    return source.Where(x => x.ShippedDate < inputdate);
                default:
                    throw new NotSupportedException();
            }
        default: break;
    }
    throw new NotSupportedException();
}
①の部分について

 IQueryableインターフェースは式ツリーを使用して動的にクエリを組み立てます。LINQ to Entitiesなどがこの仕組みを利用しています。今回はパラメータのsourceがOrdersテーブルのデータである前提での記載なので、IQueryableインターフェースのインスタンスが実行された時の型を取得するElementTypeプロパティがOrdersの場合に、OfType<T>メソッドを使用することで、IQueryableインターフェースのインスタンスを非ジェネリックIQueryable<T>のパラメータに変換してメソッドを呼び出すことができます。

 自作フィルター最大のキモはこのIQueryableインターフェースのパラメータをIQueryable<T>インターフェースのパラメータに変換する点です。

②の部分について

 IQueryable<Orders>のパラメータではWhereなどのクエリ演算子が使用できるようになります。あとは、入力された日付型と入力されたカラムを特定した上で日付比較を実施し、結果を返すだけです。

ジェネリックなフィルターと非ジェネリックなフィルター作成について

 ここでは、Dynamic Dataにおいて特定のフィルター対象で使用することを前提で作成しました。しかしこのやり方では、Ordersテーブル以外のテーブルでも日付フィルターを使用したい場合に、その数だけデータテーブル型のフィルターを作成することになる点に注意が必要です。なお、式ツリーを駆使することで、ジェネリックなフィルターを作成することもできます。ただし、ジェネリックな式ツリーを組むのは多くのXXXExpressionクラスやExpressionクラス自身について熟知する必要があります。余程の事がない限りジェネリックなフィルターではなく、非ジェネリックなフィルターを実装する方がDynamic Dataを利用する目的に沿うことになるでしょう。

 なお、式ツリーについて詳細を知りたい方はこちらを参照ください。

5.フィルターを実行するメソッドを記載

 フィルターの実装はできましたが、この状態では実行されません。以下のようにOnFilterChangedメソッドをドロップダウンリストのSelectedIndexChangedイベント内に記載することで、フィルターを利用したクエリの再実行をデータソースに通知します。

Date.ascxファイルの修正
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
    OnFilterChanged();
}

 フィルター実際の処理は記載する内容により大きく変わりますが、冒頭に記載したように/DynamicData/Filters/フォルダ内にフィルター機能のユーザーコントロールを作成し、その中でLINQでいうWhereメソッドを自作すると言う流れを押さえておくことで、より現場で活用できるフィルターを自作できるようになるでしょう。

次のページ
エンティティテンプレートのカスタマイズ

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ASP.NET Dynamic Data活用編連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト ナオキ(ナオキ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5881 2011/04/26 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング