WebSurfer's Home

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

コンパイル済みアセンブリの保存場所

by WebSurfer 2013年9月30日 14:13

ASP.NET ベースの web アプリケーションで、コンパイル済みのアセンブリが HDD のどこに保存されるかについて、調べたことを備忘録として書いておきます。

コンパイルの方法も保存場所も、Web サイトプロジェクトか Web アプリケーションプロジェクトのどちらで作っているかによって違ってきます。

Web サイトプロジェクト

Web サイトプロジェクトでは、ソースファイルをそのまま Web サーバー (IIS) の仮想ディレクトリに配置します。(プリコンパイルをしない場合です)

ユーザーが Web サイトの ASP.NET ページ (.aspx ファイル) などのリソースを最初に要求したときに、ページやコードファイルが Web サーバー (IIS) によって動的にコンパイルされます。

コンパイルされたコードは Temporary ASP.NET Files フォルダーにキャッシュされ、同じページに対するその後の要求ではキャッシュのコンパイル済みコードを使用するので、効率良く処理されます。

その詳細については、MSDN ライブラリ ASP.NET の動的コンパイルの概要 を参照してください。

Web アプリケーションプロジェクト

Web アプリケーションプロジェクトの場合は、Visual Studio を使用してプロジェクトの Bin フォルダーに、単一のアセンブリを、手動で作成します。(Web サイトプロジェクトのように Web サーバーによって自動的にコンパイルされない)

Web サイトプロジェクトと Web アプリケーションプロジェクトとの違いについては、MSDN ライブラリ Visual Studio での Web アプリケーション プロジェクトと Web サイト プロジェクト を見てください。

そのページにも書いてありますが、日本語訳がイマイチなので、原文の Compilation のセクションの説明を以下に抜粋します。

"For web application projects, you typically build the project in Visual Studio or by using the ASP.NET batch compiler on a computer that is not the production IIS server. All code-behind class files and standalone class files in the project are compiled into a single assembly, which is then put in the web application project's Bin folder. (The .aspx and .ascx files are compiled dynamically in a manner similar to what is done for web site projects.)"

つまり、コードビハンド(.aspx.cs)や独立したクラスファイルは、Visual Studio(または ASP.NET バッチ コンパイラ)を使用して、手動で単一アセンブリにコンパイルして、アプリケーションルート直下の Bin フォルダに配置します。運用環境では、その単一アセンブリを Web サーバーにコピーして使います。

一方、.aspx と .ascx(ページディレクティブと HTML デザインブロック)は、Web サイトプロジェクトと同様に、Web サーバーで動的にアセンブリにコンパイルされ、Temporary ASP.NET Files フォルダに保存されます。

コードの継承関係を見れば分かると思いますが、Page ← .aspx.cs ← .aspx となっており(矢印の方向が継承元)、実行時に両方メモリーにロードされ、合体して動作するようになっています。

Tags: ,

ASP.NET | ASP.NET

GridView 内の RadioButton

by WebSurfer 2013年8月30日 14:02

GridView の 1 行のみを選択する目的で、GridView の各行に RadioButton を配置する場合の注意点を備忘録として書いておきます。

GridView 内の RadioButton

あるラジオボタンのグループで、単一のラジオボタンしか選択できないようにするには、そのグループの中のラジオボタンの name 属性の値を同一にします。

ところが GridView に 配置した RadioButton の場合、サーバー側のコードで name 属性が同一になるように設定しても、それからレンダリングされる input 要素の name 属性の値は ASP.NET によって書き換えられ、全て異なった値になります。結果、ラジオボタンなのに複数の行が選択できてしまいます。

理由は以下の通りです。

名前付けコンテナー(INamingContainer インターフェイスを実装するコントロール)内にコントロールが配置されていると、ASP.NET は名前付けコンテナーの ID をコントロールの ID に追加して ClientID 値を生成します。

GridView の場合、その個別の行をあらわす GridViewRow が名前付けコンテナーであり、それから HTML の繰り返しブロックがレンダリングされますが、その中に配置された RadioButton には連続番号を含むプレフィックスが追加され、ページの中で一意になる(同じ ID が無い)ように ClientID 値が生成されます。

RadioButton の場合は name 属性も生成され、その値もページの中で一意になるよう、名前付けコンテナーの ID がプレフィックスとして追加されます。(ClientID とは命名規則が異なりますが、ページ内で一意になる点は同じです)

例えば、今回の記事の例の場合、GridView 内のラジオボタンの name は GridView1$ctl02$RadioButton1 というようになり、その中で ctl02 の部分が行によって異なります。

ASP.NET が name の値を生成するタイミングは、PreRender イベントが完了した後、html コードをレンダリングする時のようです。従って、それ以前にサーバー側で name 属性をいかように設定しても、ブラウザに送信される html コードは名前付けコンテナーの ID が追加されたものに書き換えられてしまいます。

なので、RadioButton.GroupName プロパティを設定するとか、GridView の RowCreated や PreRender イベントなどのハンドラで全ての RadioButton の name 属性を同一に書き換えるなどしても効果はありません。

何か方法はないか探してみましたが、サーバー側で設定する方法は見つけられませんでした。

結局、ブラウザが html コードの読み込みを完了した後、クライアントスクリプトで当該ラジオボタンの name 属性を同一に書き換えてやることで対応しました。

以下のサンプルコードのような感じです。jQuery を利用すると比較的簡単にできます。ただし、当然ですが、ブラウザで JavaScript が無効になっているとダメです。

自分が見つけてない、サーバー側のコードで対応できる、もっと簡単な方法があるかもしれません。

2013/8/31 追記:
愚直に name 属性の値を書き換えるより簡単な方法がありました。詳しくはこの記事の下の方の「------ 2013/8/31 追記 ------」を見てください。また。サーバー側だけで対応する方法も書いておきました。

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

  protected void Button1_Click(object sender, EventArgs e)
  {
    string ids = "";
    for (int i = 0; i < GridView1.Rows.Count; i++)
    {
      GridViewRow row = GridView1.Rows[i];
      RadioButton rb = 
        (RadioButton)row.FindControl("RadioButton1");
      if (rb != null)
      {
        if (rb.Checked == true)
        {
          ids += GridView1.DataKeys[i].Value.ToString() + " ";
        }
      }
    }
    Label1.Text = "Setected product(s): " + ids;
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="Scripts/jquery-1.8.3.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    // サーバー側で設定されたラジオボタンの name 属性
    // のオリジナル値を保存しておくための配列。
    var originalNames;

    // ラジオボタンの name 属性の元の値を配列に保存した
    // 上で、同一(この例では radiobutton1)に書き換え。
    $(function () {
      originalNames = new Array();
      $('#<%=GridView1.ClientID%> input:radio').each(
        function () {
          originalNames.push($(this).attr('name'));
          $(this).attr('name', 'radiobutton1');
        });
    });

    // ポストバックする前に、ラジオボタンの name 属性を
    // オリジナル値に戻すためのメソッド。オリジナル値に
    // 戻さないとチェックされたラジオボタンをサーバー側
    // で取得できず、かつ、再描画されたときにチェックマ
    // ークがつかない。
    // この例では Button1.OnClientClick プロパティにこの
    // メソッドを設定している。
    function RetrieveOriginalNames() {
      $('#<%=GridView1.ClientID%> input:radio').each(
        function (n) {
          $(this).attr('name', originalNames[n]);
        });
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice] 
        FROM [Products] 
        WHERE ([CategoryID] = @CategoryID)">
      <SelectParameters>
        <asp:Parameter DefaultValue="1" 
          Name="CategoryID" Type="Int32" />
      </SelectParameters>
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="ProductID" 
      DataSourceID="SqlDataSource1">
      <Columns>
        <asp:TemplateField HeaderText="選択">
          <ItemTemplate>
            <asp:RadioButton ID="RadioButton1"
              runat="server" />
          </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="ProductID" 
          HeaderText="ProductID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" 
          HeaderText="ProductName" 
          SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" 
          HeaderText="UnitPrice" 
          SortExpression="UnitPrice" />
      </Columns>
    </asp:GridView>

    <asp:Button ID="Button1" runat="server" 
      Text="Button" 
      OnClick="Button1_Click" 
      OnClientClick="RetrieveOriginalNames();" />
    <br />
    <asp:Label ID="Label1" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

------ 2013/8/31 追記 ------

name 属性は書き換えないでそのままにしておいて、あるラジオボタンをチェックしたら他のラジオボタンのチェックを外すようにする方が簡単でした。以下のような感じです。

<script src="Scripts/jquery-1.8.3.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
  // ラジオボタンがチェックされたら、一旦全てのラジオ
  // ボタンのチェックを外し、チェックされたラジオボタ
  // ンに改めてチェックを入れ直す方がはるかに簡単。
  $(function () {
    $('#<%=GridView1.ClientID%> input:radio').change(
      function () {
        $('#<%=GridView1.ClientID%> input:radio').
          removeAttr("checked");
        $(this).attr('checked', 'checked');
      });
  });

  // Button1 クリックで name を元の値に書き戻す必要は
  // なくなる。
//]]>
</script>

その他、クライアントスクリプトを使わないでサーバー側だけで対応する方法として、RadioButton を継承したカスタムコントロールを作る方法があります。その例は以下のページを見てください。

データバインドコントロール内で使用できるカスタムラジオボタンの作成

DataGrid内のラジオボタンでグループに出来ない問題の回避方法

Tags: ,

ASP.NET

Object 型と列挙型引数のオーバーロード

by WebSurfer 2013年8月10日 12:48

SqlParameterCollection.Add メソッド には以下のオーバーロードがあります。

  • Add (Object)
  • Add (SqlParameter)
  • Add (String, SqlDbType)
  • Add (String, Object)
  • Add (String, SqlDbType, Int32)
  • Add (String, SqlDbType, Int32, String)

このうち、Add(String, Object) は Add(String, SqlDbType) との曖昧さの問題で廃止され、代わりに AddWithValue メソッド の使用が推奨されています。

何が曖昧かと言うと、MSDN ライブラリの SqlParameterCollection.Add メソッド (String, Object) の説明に書いてありますように、第 2 引数に 0(整数ゼロ)を渡すと、Add (String, Object) ではなくて、Add (String, SqlDbType) オーバーロードが呼び出されることです。

第 2 引数に 0 を渡せばもちろん、(int)0 でも Add (String, SqlDbType) オーバーロードが呼び出されます。

Add (String, Object) オーバーロードを呼び出すには、第 2 引数に Convert.ToInt32(0) または (object)0 を渡さなければなりません。

何故でしょう? 以下のような理由だと思います。(多少推測があります)

まず、SqlDbType が列挙型というところが要注意です。MSDN ライブラリ Enumeration Types (C# Programming Guide) によると、0(整数ゼロ)は列挙型のデフォルト値とのことで、特別な意味を持つようです。

それゆえ、0 や (int)0 は SqlDbType 列挙型のデフォルト値と判断されるらしく、Add (String, Object) ではなくて、Add (string, SqlDbType) オーバーロードが呼ばれます。

Convert.ToInt32(0) は(Object 型から派生した)int 型、(object)0 は Object 型と判断されて、Add (String, Object) オーバーロードが呼ばれます。

ちなみに、もし Add (String, Int32) というオーバーロードがあったとすると、第 2 引数は 0 でも (int)0 でも Convert.ToInt32(0) でも、Add (String, Int32) オーバーロードが呼ばれるはずです。

検証するため以下のサンプルを作ってみましたが、サンプルコード内のコメントにあるように期待通りの結果となりました。

namespace MyConsoleApplication
{
    enum Days { Sat, Sun, Mon, Tue, Wed, Thu, Fri };

    class TestClass
    {
        public void Add1(object obj)
        {
            Console.WriteLine("Add1(object)");
        }

        public void Add1(Days d)
        {
            Console.WriteLine("Add1(Days)");
        }

        public void Add2(object obj)
        {
            Console.WriteLine("Add2(object)");
        }

        public void Add2(Days d)
        {
            Console.WriteLine("Add2(Days)");
        }

        public void Add2(int i)
        {
            Console.WriteLine("Add2(int)");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TestClass tc = new TestClass();

            tc.Add1(0);
            tc.Add1(1);
            tc.Add1((int)0);
            tc.Add1(Convert.ToInt32(0));
            tc.Add1((object)0);

            Console.WriteLine("--------------------");

            tc.Add2(0);
            tc.Add2(1);
            tc.Add2((int)0);
            tc.Add2(Convert.ToInt32(0));
            tc.Add2((object)0);

            // 結果は:

            // Add1(Days)
            // Add1(object)
            // Add1(Days)
            // Add1(object)
            // Add1(object)
            // --------------------
            // Add2(int)
            // Add2(int)
            // Add2(int)
            // Add2(int)
            // Add2(object)
        }        
    }
}

Tags: ,

.NET Framework

About this blog

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

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar