Archive

Archive for 2009年9月

ちょっと先取り Routing 4:その5 RouteParameter

この記事は その1 その2 その3 その4 の記事を見ていることを前提にしています。

RouteParameterを用意すると、DataSource等のコントロールにそのままパラメータとしてRouteDataを渡すことができるようになります。

ここでもweb.configの修正はあるのですが、その前にRouteParameter.csファイルを追加します。
このファイルに以下のプログラムを記述します。

namespace Samples.Routing
{
    using System;
    using System.Web;
    using System.Web.Routing;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Data;

    public class RouteParameter : Parameter
    {
        public RouteParameter()
        {
        }

        public RouteParameter(string name, string routekey)
            : base(name)
        {
            this.RouteKey = routekey;
        }

        public RouteParameter(string name, DbType dbtype, string routekey)
            : base(name, dbtype)
        {
            this.RouteKey = routekey;
        }

        public RouteParameter(string name, TypeCode type, string routekey)
            : base(name, type)
        {
            this.RouteKey = routekey;
        }

        protected RouteParameter(RouteParameter original)
            : base(original)
        {
            this.RouteKey = original.RouteKey;
        }

        protected override Parameter Clone()
        {
            return new RouteParameter(this);
        }

        public string RouteKey
        {
            get
            {
                object o = base.ViewState["RouteKey"];
                if (o == null)
                    return String.Empty;
                return (string)o;
            }
            set
            {
                base.ViewState["RouteKey"] = value;
                base.OnParameterChanged();
            }
        }

        protected override object Evaluate(HttpContext context, Control control)
        {
            if ((context != null) && (context.Request != null))
            {
                var r = RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
                return r != null ? r.Values[this.RouteKey].ToString() : string.Empty;
            }
            return null;
        }

    }
}

このプログラムを記述するにあたっては、QueryStringParameterクラスの実装を参考にしました。
次にweb.configのsystem.web/pages/controls要素に以下の通りに追加します。

<add tagPrefix="asp" namespace="Samples.Routing" />

実はRouteParameter.csのプログラム中でnamespaceを設定しているのが重要な点です。
web.conifgにtagPrefixの設定をする場合、Webサイトプロジェクトに追加したクラスならnamespaceさえ記述してあればこんなに簡単にtagPrefixの設定ができます。
この設定を行うことで、まるで元から用意されているコントロールのように、<asp: というタグを使えるようになります。

さて、ここまでで準備は終了。
適当なデータを用意し、Default2.aspxに以下のようにGridViewとSqlDataSourceを追加して動作を確認してみましょう。

    <asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" />
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:ConnectionString %>" SelectCommand="SELECT * FROM [Table1] WHERE ([ID] = @ID)">
        <SelectParameters>
            <asp:RouteParameter Name="ID" RouteKey="id" />
        </SelectParameters>
    </asp:SqlDataSource>

これでURLに対応したIDのデータのみが選択されて表示されればテストは終了です。

実はRouteParameterのかわりにRouteValue expressionを使っても同じことはできます。

<asp:Parameter Name="ID" DefaultValue="<%$ RouteValue : id %>" />

こんな感じで使えばいいですね。
まぁ、せっかく用意したRouteParameterなので、使えるところはこっちを使うほうがいい、、、のかな(w

カテゴリー:.NET, ASP.NET 備忘録

ちょっと先取り Routing 4:その4 RouteValue expression

この記事は その1 その2 その3 の記事を見ていることを前提にしています。
その3の記事とほとんど同じように見えるので、どこが違うのか注意して読んでください。

RouteValue expressionはRouteDataをaspxページ内で定義されたコントロールに渡す手段を提供します。

web.configのsystem.web/compilation/expressionBuilders要素に以下を追加します。

<add expressionPrefix="RouteValue" type="RouteValueExpressionBuilder"/>

そして、新しいクラスとしてRouteValueExpressionBuilder.csファイルを追加します。
このファイルには以下のプログラムを記述します。

using System;
using System.CodeDom;
using System.ComponentModel;
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
using System.Web.UI;

[ExpressionPrefix("RouteValue")]
public class RouteValueExpressionBuilder : ExpressionBuilder
{
    public static object GetEvalData(string expression, Type target, string entry)
    {
        var r = RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
        return r != null ? r.Values[expression].ToString() : string.Empty;
    }

    public override object EvaluateExpression(object target, BoundPropertyEntry entry,
    object parsedData, ExpressionBuilderContext context)
    {
        return GetEvalData(entry.Expression, target.GetType(), entry.Name);
    }

    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {
        Type type1 = entry.DeclaringType;
        PropertyDescriptor descriptor1 = TypeDescriptor.GetProperties(type1)[entry.PropertyInfo.Name];
        CodeExpression[] expressionArray1 = new CodeExpression[3];
        expressionArray1[0] = new CodePrimitiveExpression(entry.Expression.Trim());
        expressionArray1[1] = new CodeTypeOfExpression(type1);
        expressionArray1[2] = new CodePrimitiveExpression(entry.Name);
        return new CodeCastExpression(descriptor1.PropertyType, new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(base.GetType()), "GetEvalData", expressionArray1));
    }

    public override bool SupportsEvaluate
    {
        get { return true; }
    }
}

RouteUrlExpressionBuilderと同じく、GetEvalDataメソッドの中身以外はExpressionBuilderクラスのサンプルプログラムそのものです。

さて、ここまでで準備は終了。
Default2.aspxに以下の記述を追加して、デバッグを開始してみます。

<asp:Label ID="Label2" runat="server" Text="<%$ RouteValue : id %>" />

Default.aspxにはその3で試したリンクが表示されるので、そのリンクをクリックして以下のURLに遷移します。

http://localhost:(port)/RoutingTest/test/3

このとき、上で追加したLabel2に"3"が表示されればテストは終了です。

RouteValue expressionを使うと、このようにaspxページ内のコントロールのプロパティにRouteDataを直接設定できます。
DataSourceコントロールとともにRouteValue expressionを利用すると、コードを記述することなくURLに合わせたデータを表示することができるようになります。

カテゴリー:.NET, ASP.NET 備忘録

ちょっと先取り Routing 4:その3 RouteUrl expression

この記事は その1 その2 の記事を見ていることを前提にしています。

RouteUrl expressionはRouting用のURL設定をaspxページ内で定義されたコントロールに渡す手段を提供します。

やはりweb.configに設定が必要になってきますので、system.web/compilation要素に以下を追加します。

<expressionBuilders>
    <add expressionPrefix="RouteUrl" type="RouteUrlExpressionBuilder"/>
</expressionBuilders>

そして、新しいクラスとしてRouteUrlExpressionBuilder.csファイルを追加します。
このファイルには以下のプログラムを記述します。

using System;
using System.CodeDom;
using System.Web.UI;
using System.ComponentModel;
using System.Web.Compilation;
using System.Web.Routing;

[ExpressionPrefix("RouteUrl")]
public class RouteUrlExpressionBuilder : ExpressionBuilder
{
    public static object GetEvalData(string expression, Type target, string entry)
    {
        string[] expressionArray = expression.Split(‘=’);
        return RouteTable.Routes.GetVirtualPath(null, new RouteValueDictionary { { expressionArray[0], expressionArray[1] } }).VirtualPath;
    }

    public override object EvaluateExpression(object target, BoundPropertyEntry entry,
    object parsedData, ExpressionBuilderContext context)
    {
        return GetEvalData(entry.Expression, target.GetType(), entry.Name);
    }

    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {
        Type type1 = entry.DeclaringType;
        PropertyDescriptor descriptor1 = TypeDescriptor.GetProperties(type1)[entry.PropertyInfo.Name];
        CodeExpression[] expressionArray1 = new CodeExpression[3];
        expressionArray1[0] = new CodePrimitiveExpression(entry.Expression.Trim());
        expressionArray1[1] = new CodeTypeOfExpression(type1);
        expressionArray1[2] = new CodePrimitiveExpression(entry.Name);
        return new CodeCastExpression(descriptor1.PropertyType, new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(base.GetType()), "GetEvalData", expressionArray1));
    }

    public override bool SupportsEvaluate
    {
        get { return true; }
    }
}

GetEvalDataメソッドの中身以外はExpressionBuilderクラスのサンプルプログラムそのものです。

さて、ここまでで準備は終了。
Default.aspxに以下の記述を追加して、デバッグを開始してみます。

<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="<%$ RouteUrl: id=3 %>">RouteUrlテスト</asp:HyperLink>

正しくリンクが表示され、リンクをクリックすることで以下のURLに遷移できればテストも完了です。

http://localhost:(port)/RoutingTest/test/3

RouteUrl expressionを使うと、このようにaspxページ内のコントロールのプロパティにRouting用のURLを設定できます。
ただこれでできるのは静的な設定だけなので、実は直接URLを書いてしまってもいいような気がしています。

カテゴリー:.NET, ASP.NET 備忘録

ちょっと先取り Routing 4:その2 Page.RouteData じゃなくて Page.GetRouteData()

この記事は その1 の記事を前提にしています。
まずはそちらを見てください。

ASP.NET 4 で予定されている Page.RouteData という形でPageのプロパティとしてRoutingのデータを取り出すには、HttpContextクラスやPageクラス自体に変更を加える必要があります。
そこで、ここでは拡張メソッドを利用してPageクラスにRoutingデータを取り出すGetRouteDataメソッドを追加することにしました。

web.configファイルの system.web/compilation/assemblies 要素に以下の記述を追加します。

<add assembly=”System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/>

このdll参照設定がないと、コンパイル時にアセンブリが足りない、といったエラーになります。

さて、PageExtentionクラスを追加し、以下のとおりに記述します。

using System.Web;
using System.Web.UI;
using System.Web.Routing;

public static class PageExtention
{
    public static string GetRouteData(this  Page p, string term)
    {
        var r =  RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
        return r != null ? r.Values[term].ToString() : string.Empty;
    }
}

これで準備完了。
Default2.aspxにLabelを貼り付けて、コードビハインド側に以下のように記述しましょう。

protected void Page_Load(object sender, EventArgs e)
{
    Label1.Text = “ID: ” + this.GetRouteData(“id”);
}

デバッグを実行して、次のURLにアクセスします。

http://localhost:(port)/RoutingTest/test/3

間違えたところがなければ、ブラウザ上には「ID:3」と表示されるはずです。
では、次のURLだとどうなるでしょう。

http://localhost:(port)/RoutingTest/test/

実はこの場合、「ID:1」と表示されます。
その1 の記事の中でRoutingの設定を行っていますが、そこでidのデフォルト値として1を設定していますので、こんな結果がでることになります。

カテゴリー:.NET, ASP.NET 備忘録

ちょっと先取り Routing 4:その1 PageRouteHandler

ASP.NET 4 のホワイトペーパーを見ると、Routingに関して以下の機能が追加されています。

・MapPageRoute
特定のRoutingパターンに対して特定のページを設定する。

・Page.RouteData
RouteDataをページ内のプログラムで利用するために取得する手段を提供する。

・RouteUrl expression
Routing用のURL設定をaspxページ内で定義されたコントロールに渡す手段を提供する。

・RouteValue expression
RouteDataをaspxページ内で定義されたコントロールに渡す手段を提供する。

・RouteParameter
RouteDataをDataSourceコントロールにParameterとして渡す手段を提供する。

これらの機能を用いることでRoutingを気軽に利用できるようになると考えられます。
しかし、こんな便利そうな機能セット、なんとか3.5 SP1の環境で利用できないものでしょうか。

ということで、上記の機能そのものではないにしても、同じような機能を用意し、利用する方法を探ってみます。
第1回目としてはMapPageRouteのベースと考えられるPageRouteHandlerを作成します。

まず最初にRoutingを利用するための環境を整えましょう。
新しいWebサイト(とりあえずRoutingTestという名前をつけます)を作成します。
web.configファイルの system.web/compilation/assemblies 要素に以下の記述を追加します。

<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

これは System.Web.Routing.dllを利用する、という設定になります。
それから同じくweb.configファイルの system.web/httpmodules 要素に以下の記述を追加します。

<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

これは Routing用のModule(UrlRoutingModule)を利用する、という設定です。
とりあえずVisualStudio や Visual Web Developterのテスト環境では、以上の設定でRoutingが利用できるようになります。

※IIS7以降の環境で動作させるためには こちらのMSDNのページを参考にしてください。

次に、PageRouteHandlerクラスを作成します。
新しいクラスとしてPageRouteHandler.csファイルを追加しましょう。
このファイルには以下のプログラムを記述します。

 

using System.Web;
using System.Web.Routing;
using System.Web.Compilation;
using System.Web.UI;

public class PageRouteHandler : IRouteHandler
{
    string virtualPath = string.Empty;

    public PageRouteHandler(string vPath)
    {
        virtualPath = vPath;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as IHttpHandler;
    }
}

 

さて、このPageRouteHandlerクラスが本当に動作するかどうか確認してみましょう。
グローバルアプリケーションクラス(Global.asax)を追加して、2行めに以下を記述します。

<%@ Import Namespace="System.Web.Routing" %>

これでGlobal.asaxの中でSystem.Web.Routingネームスペースが利用できるようになります。
Application_Startメソッドに以下を記述します。

RouteTable.Routes.Add("testRoute",
                                    new Route("test/{id}", 
                                    new RouteValueDictionary(new { id = 1 }),
                                    new PageRouteHandler("~/Default2.aspx")));

プロジェクト内にDefault2.aspxを追加して何か目印になる文字列を書いておき、デバッグを実行して以下のようなURLにアクセスしてみましょう。

http://localhost:(port)/RoutingTest/test/

ここで(port)の部分は実行している環境によって異なります。
このURL、つまりWebアプリケーションのルートに test を加えたURLでDefault2.aspxの内容が表示されればPageRouteHandlerクラスが正しく動作しているということになります。

PageRouteHandlerを使う場合、1つのページ毎にRoutingの設定を追加して利用することになります。
Routingの設定数は増えますが、そのほうがぱっと見てわかりやすい、ということがあるかもしれません。

カテゴリー:.NET, ASP.NET 備忘録