WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

ジェネリックハンドラー

by WebSurfer 2014年12月16日 14:42

Web サイトプロジェクトでジェネリックハンドラー(.ashx)を作成する際、コードビハインド形式では作成できないのですが、それは何故かについて書きます。(どうでもいい話かもしれませんが)

ジェネリックハンドラーの作成

上の画像は Visual Studio 2010 のソリューションエクスプローラーから「新しい項目の追加」ダイアログを開いてジェネリックハンドラーを追加しようとしているところです。[別のファイルにコードを書き込む(P)]のオプションがグレーアウトされて選択できないのがわかるでしょうか? そのまま作業を進めると、.ashx ファイルのみが生成されます。

一方、Web アプリケーションプロジェクトの場合は[別のファイルにコードを書き込む(P)]というようなオプションはそもそも表示されません(ジェネリックハンドラに限らず Web フォームなど他の項目も同様ですが)。自動的にコードビハインド形式になります(.ashx ファイルと .ashx.cs ファイルが作成されます)。

(注:Web アプリケーションプロジェクトと Web サイトプロジェクト の違いについては MSDN ライブラリの記事をリンクしたのでそこを見てください)

Web サイトプロジェクトでコードビハインド形式で作成できない理由が書いてある Microsoft の文書は見つけられなかったのですが、MSDN ライブラリ HTTP ハンドラの概要 のページの「ファイル名拡張子の作成」のセクションの以下の記述が関係ありそうです。

"ファイル名拡張子 .ashx に対応する HTTP ハンドラクラスを作成した場合、ハンドラは IIS と ASP.NET に自動的に登録されます。"

"ハンドラに対応するカスタムファイル名拡張子を作成する場合は、この拡張子を IIS と ASP.NET に明示的に登録する必要があります。"

引用文の前者がこの記事で言うジェネリックハンドラーのことで、HTTP ハンドラ本体(IHttpHandler を継承するクラス)とハンドラマッピング(@ WebHandler ディレクティブと Class 属性定義)を同一ファイル内に実装して .ashx という拡張子を付与し、IIS と ASP.NET に自動的に登録されるようにしたものです。

引用文の後者は、一般的な HTTP ハンドラのことで、HTTP ハンドラ本体を普通のクラスファイルに実装し、ソースコードのまま App_Code フォルダに置くか、コンパイルして dll を作って Bin フォルダに配置し、web.config にハンドラマッピングを定義するということになります。

つまり、ジェネリックハンドラーは、いちいち web.config にハンドラマッピングの定義を書かなくても、例えば <img src=handler.ashx ... /> のようにするだけで簡単に使えます。

コードビハインド形式にする(.ashx ファイルと .ashx.cs ファイルに分ける)のは取り扱いが面倒になるだなので、Web サイトプロジェクトでジェネリックハンドラーをコードビハインド形式で作成するオプションがないのだと思います。

Web アプリケーションプロジェクトではコード部分を分けざるを得ませので、選択の余地なく .ashx ファイルと .ashx.cs ファイルが分けて生成されます。ただし、Visual Studio からは .ashx.cs ファイルしか開けません(ファイルは存在しますが Visual Studio 上では開けません)。


以下は余談ですが、「コードビハインド」という呼び方について一言・・・

本来、HTML デザインブロックの背後にある ASP.NET 処理ブロック(.aspx.cs, .master.cs, .ascx.cs などに定義されるクラス)をコードビハンドと呼ぶようです。なので、.ashx ファイルの場合はコードビハンドという呼び方ではなく「HTTP ハンドラ本体」と言う方が適切かもしれません。

また、@ Page ディレクティブの CodeBehind 属性の説明を読むと "この属性は、以前のバージョンの ASP.NET との互換性を保持して、分離コード機能を実装するために用意されています。ASP.NET Version 2.0 では、代わりに CodeFile 属性を使用してソース ファイルの名前を指定すると同時に、Inherits 属性を使用してクラスの完全修飾名を指定します" とのことです。

@ WebHandler ディレクティブにも CodeBehind 属性はありますが、MSDN ライブラリによると "この属性は実行時には使用されません。この属性は、以前のバージョンの ASP.NET と互換性を保つために含まれています" とのことです。

なので「コードビハインド」という呼び方自体が古いのかもしれません。

Tags:

ASP.NET

GridView の Pager に総レコード数を表示

by WebSurfer 2014年12月6日 18:26

以下の画像のように、ページングが有効になっている GridView の Pager 内に「総レコード数」を表示するにはどうしたらいいかということを書きます。

Pager に総レコード数を表示

ListView と DataPager を利用する場合は、先の記事 ListView でページ指定 のサンプルコードのように Container.TotalRowCount で総レコード数が取得できます。(注:Container は DataPager オブジェクトを参照しています)

しかしながら、GirdView を利用した場合、ページング機能は GirdView に統合されているものを利用することになり、それから「総レコード数」を取得するプロパティなどはありません。

GridView は、データーソースコントロール(SqlDataSource または ObjectDataSource)と組み合わせて使うケースがほとんどだと思いますが、その場合はデーターソースコントロールの Selected イベントのハンドラを使って「総レコード数」を取得するのが簡単そうです。

例えば、データーソースコントロールに SqlDataSource を使う場合、その Selected イベントのハンドラの引数 SqlDataSourceStatusEventArgs クラスAffectedRows プロパティ から総レコード数を取得できます。

取得した「総レコード数」をページャーの中に表示するには、GridView.RowDataBound イベント のハンドラでページャーの行を探して、その中に追加します。

ページャーそのものはデフォルトで(何も設定しなくても) 1 2 3 4 5 6 7 8 9 10 ... というようなクリックするとその番号のページに飛べるハイパーリンクが生成されますが、PagerSettings クラス を使うともう少し細かな設定ができます。

上の画像のページャーは、PagerSettings クラスの Mode プロパティを NumericFirstLast に、PageButtonCount プロパティを 5 に設定した場合のものです。

SqlDataSource と GridView を使った場合のサンプル(上の画像のページを表示したもの)を以下にアップしておきます。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  int GridView3TotalCount = 0;
    
  protected void SqlDataSource1_Selected(
        object sender, SqlDataSourceStatusEventArgs e)
  {
      GridView3TotalCount = e.AffectedRows;
  }

  protected void GridView3_RowDataBound(
        object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Pager)
    {
      Label label = new Label();
      label.Text = "総レコード数: " + 
            GridView3TotalCount.ToString();
      e.Row.Cells[0].Controls.AddAt(0, label);
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>GridView Total Records</title>
</head>
<body>
  <form id="form1" runat="server">
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand="SELECT * FROM [Orders]" 
      OnSelected="SqlDataSource1_Selected">
    </asp:SqlDataSource>

    総レコード数: <%=GridView3TotalCount%>
    <asp:GridView ID="GridView3" runat="server" 
      AllowPaging="True" 
      AutoGenerateColumns="False" 
      DataKeyNames="OrderID" 
      DataSourceID="SqlDataSource1" 
      OnRowDataBound="GridView3_RowDataBound">
      <PagerSettings 
        Mode="NumericFirstLast" 
        PageButtonCount="5">
      </PagerSettings>
      <Columns>
        <asp:BoundField DataField="OrderID" 
          HeaderText="OrderID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="OrderID" />
        <asp:BoundField DataField="CustomerID" 
          HeaderText="CustomerID" 
          SortExpression="CustomerID" />
        <asp:BoundField DataField="OrderDate" 
          HeaderText="OrderDate" 
          SortExpression="OrderDate" 
          DataFormatString="{0:yyyy/MM/dd}" />
        <asp:BoundField DataField="Freight" 
          HeaderText="Freight" 
          SortExpression="Freight" 
          DataFormatString="${0:N2}" />
        <asp:BoundField DataField="ShipCountry" 
          HeaderText="ShipCountry" 
          SortExpression="ShipCountry" />
      </Columns>
    </asp:GridView>
  </form>
</body>
</html>

データーソースコントロールに ObjectDataSource を使う場合も、ObjectDataSource.Selected イベントのハンドラ で「総レコード数」を取得できます。

ObjectDataSource を使う場合、ページを切り替えるたびに全レコードを DB から再度取得するという無駄なことを避けるため、普通は、先の記事 ObjectDataSource でページング に書いてあるように、必要なレコードのみ DB から取得するクラス/メソッドを実装して、それらを ObjectDataSource で定義されているプロパティに設定することになると思います。

上に紹介した記事のサンプルコードでは、GetDataByIndex と GetNumberOfMessages の 2 つのメソッドが ObjectDataSource に設定されていますが、その両方が呼ばれ、それぞれで Selected イベントが発生します(2 回 Selected イベントが発生します)。

その際、イベントハンドラの引数 ObjectDataSourceStatusEventArgs の ReturnValue プロパティ でそれぞれのメソッドが返すオブジェクトを取得できます。

したがって、e.ReturnValue が Int32 型にキャストできれば GetNumberOfMessages が返したもの、即ち全レコード数ということになります。なので、イベントハンドラのコードを以下のようにすれば「総レコード数」を取得できます。

protected void ObjectDataSource1_Selected(object sender, 
                        ObjectDataSourceStatusEventArgs e)
{
  if (e.ReturnValue is Int32)
  {
    GridView3TotalCount = (Int32)e.ReturnValue;
  }
}

「総レコード数」さえ取得できれば、あとは GridView と Pager の実装を SqlDataSource を使った場合と同様にして Pager に表示できます。

Tags: ,

Paging

MySQL と SqlDataSource の問題

by WebSurfer 2014年11月24日 17:10

Visual Studio 2010 Professional 上で MySQL と SqlDataSource を使って ASP.NET Web Forms アプリを作った時に遭遇した問題を書きます。

クエリのテスト結果

なお、使ったのは 3 年以上前にインストールした MySQL Community Server 5.5.11 と Connector/Net 6.3.6 ですので、最新版では様子が違うかもしれません。ご注意ください。

問題は、簡単に書くと、Visual Studio の「データソースの構成」ウィザードを使ってクエリを自動生成すると、識別子がスクエアブラケット( [ ] )で囲まれますが、それが MySQL では使えないので、上の画面のようなエラーが出るということです。

具体的な再現手順を書くと、(1) Visual Studio のデザイン画面で SqlDataSource の > 印をクリック、(2) 表示される「SqlDataSource タスク」メニューの中の[データ ソースの構成...]をクリック、(3) ウィザードの「Select ステートメントの構成」画面で[テーブルまたはビューから列を指定します(T)]を選択、(4) 次の画面「クエリのテスト」に進んでテストを実行、(5) 上の画面のようなエラーが出る・・・ということです。

上のエラーメッセージで、テーブル名 employees がスクエアブラケット( [ ] )で囲まれているのが分かるでしょうか? ちなみに、ウィザードで生成されたソースは以下のようになります。(注:この例では列に * を選択しています。実際の列を指定すると列名もスクエアブラケットで囲まれます)

<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
  ConnectionString="<%$ ConnectionStrings:MySQL %>" 
  ProviderName="<%$ ConnectionStrings:MySQL.ProviderName %>" 
  SelectCommand="SELECT * FROM [employees]">
</asp:SqlDataSource>

もちろんこの状態で実行しても以下のようにサーバーエラーになります。

サーバーエラー

解決策は、生成されたソースのクエリからスクエアブラケットを削除するか、スクエアブラケットに換えてバッククォート( ` )で囲むことです(識別子が予約語と同じ、または特殊文字を含む場合)。

または、上の再現手順の (3) のステップで[カスタム SQL ステートメントまたはストアド プロシージャを指定する(S)]を選んで、自力でクエリを組み立ててもいいです。

ただし、SELECT だけならそれでもいいですが、INSERT, DELETE, UPDATE も必要な場合は少々面倒です。

Visual Studio のデータスース構成ウィザードを使って型付 DataSet + TableAdapter を作った場合は、識別子は正しくバッククォート( ` )で囲まれ上記のような問題はありませんので、それと ObjectDataSource を組み合わせて使う方が良さそうです。

Tags:

MySQL

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar