Hatena::ブログ(Diary)

Kazzzの日記 このページをアンテナに追加 RSSフィード

2009-10-12

[][]ASP.NET MVCソースコードデバッグ(トレース)する


ASP.NET MVCの素晴らしい特徴の一つはオープンソースであり、ソースコードが公開されていることである。
ビューからポストされたHTML Formデータから、生成したモデルクラスプロパティがどのようにセットされているのかを見たくて、ASP.NET MVCのソースコードを使ってデバッグトレースしてみようと思ったが、公開されているソースコードが含まれるプロジェクト自分の作ったプロジェクトから参照するだけでは上手くいかない。

具体的には、起動時に以下の例外が発生して開始ページが表示できないのだ。
f:id:Kazzz:20091011211040p:image

最初は、元々のASP.NET MVCのアセンブリは厳密な名前署名されており(つまりGACのアセンブリを参照している)、その参照が残っているか、又はコピーされたアセンブリがbin等に残っているせいだと思ったのだが、ソリューションクリアにしても状況が変わらなかった。

そこでweb.configを見てみたのだが、案の定、コンパイルに必要なアセンブリはGACのものが指定されていた。(普通そうだけど...だからGACは嫌いだ)

  • プロジェクト直下のweb.config
<compilation debug="true">
  <assemblies>
    <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
  </assemblies>
</compilation>

該当のアセンブリを厳密名無しのものも使えるように変更してみる。

    
    <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

これで起動できるようになった。

しかし、自分が用意したビューを表示させようと該当のビューのURLを叩くと、今度は以下の例外が発生した。
f:id:Kazzz:20091011211041p:image

そのまま進めると、ビューには以下のエラーが表示される。
f:id:Kazzz:20091011211043p:image

どうやらビューのスクリプトレット構文が正しくコンパイルできていないようだが、今度はどこを直せば良いのかさっぱりだ。

これでお手上げかと思ったのだが、調べてみるとApress社が出版するPro ASP.NET MVCの著者であるSteve Sanderson氏のブログにそのものずばりのエントリがあった。

Using the ASP.NET MVC source code to debug your app « Steve Sanderson’s blog

このエントリによると、強く型付けされたビューを使う場合、プロジェクトのViewディレクトリ下にもweb.configがあり、その中のpageParserFilterTypeが属するのアセンブリ(System.Web.Mvc)もGACのが指定されているので、そこも同様に修正が必要だとのこと。

  • Views下のweb.config
<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
  <controls>
    <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

pageエレメントの'System.Web.Mvc'のアセンブリのPublicKeyTokenを全てnullに修正した。

  • Views下のweb.config(非GAC)
<pages
    validateRequest="false"
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
  <controls>
    <add assembly="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" namespace="System.Web.Mvc" tagPrefix="mvc" />
  </controls>
</pages>

これでやっと目的のことができるようになった。(デプロイ時にはアセンブリの指定をきちんと元に戻す必要がある、かな)

f:id:Kazzz:20091011211042p:image

モデルクラスのNameプロパティがセットされようにとしている所だが、同クラスの型付けを施されたViewからポスト時に、

ハンドラのリクエスト処理

コントローラアクション処理

モデルバインダによるパラメタバインド

DefaultModelBinderのSetPropertyメソッド

と呼ばれていることが解る。

ASP.NET MVCは1から書かれたものではなく、ASP.NETを拡張して作られているため比較的コード量も少ない。仕組みを知るために実行したり読んだりするのに最適だ。

2009-10-03

[][]ASP.NETでExcelシート(XMLスプレッドシート)を生成する


昔と違ってオートメーションで苦労することは無くなった。
今であればOpenXML APIを使う、LINQ to XMLを使う、XPath+XSLを使う、等、方法としてはいろいろな選択しがあると思うが、一番簡単なのは以下の方法。


1. Excel(2003以降)で実際に使用するワークシートを作る(これを雛形にする)
2. XMLスプレッドシート(Excel 2007ではXMLスプレッドシート 2003)形式で保存する※
3. 保存したXMLスプレッドシートをテキストエディタ等で開く
4. ASP.NETのビューページ(Download.aspx)を生成してコンテンツ部分にXMLスプレッドシートを貼り付ける

  • Download.aspxの冒頭

f:id:Kazzz:20091003175021p:image

これでOK。ビューをレンダリングすればXMLスプレッドシートのダウンロードが開始される。

  • AddressControler.cs
    public ActionResult Download()
    {
        return View("Download");
    }

ダウンロード時にExcelを起動したいのならビュー側でMIMEを指定すれば良い。(コントローラ側で指定しても何故か上手くいかない。ビューの初期化でヘッダを設定しているのか?)

<%@ Page Language="C#"  ContentType="application/vnd.ms-excel" 〜


また、このXMLスプレッドシートをテンプレートとして、特定の行(Row)やセル(Cell)を置き換えることで、ダイナミックにExcel帳票を作成することもできる。

  • AddressControler.cs
    public ActionResult Download()
    {
        //LINQ to SQLでAddressオブジェクトのリストを作る
        var addresses = service.FindAllAddresses().ToList();
        return View("Download", addresses);
    }

  • Download.aspx
<%@ Page Language="C#"  ContentType="application/vnd.ms-excel" Inherits="System.Web.Mvc.ViewPage<IEnumerable<AddressBook.Models.Address>>" %>
〜
 <Worksheet ss:Name="Sheet1">                            <!-- 行数をモデルの行数+5に設定(5はヘッダ行数) -->
  <Table ss:ExpandedColumnCount="6" ss:ExpandedRowCount="<%= Model.Count()+5 %>" x:FullColumns="1"
〜
 <!-- 行データをモデルオブジェクトのリストで置き換える --> 
 <% foreach (var item in Model) { %>
   <Row ss:AutoFitHeight="0">
    <Cell><Data ss:Type="String"><%=item.Name.TrimEnd()%></Data></Cell>
    <Cell><Data ss:Type="String"><%=item.Address.TrimEnd()%></Data></Cell>
    <Cell><Data ss:Type="String"><%=item.Email.TrimEnd()%></Data></Cell>
    <Cell><Data ss:Type="String"><%=item.Gender.TrimEnd()%></Data></Cell>
    <Cell><Data ss:Type="String"><%=item.Married.TrimEnd()%></Data></Cell>
    <Cell><Data ss:Type="String"><%=item.UpdDate.ToString("s").TrimEnd()%></Data></Cell>
   </Row>
 <% } %>

ASP.NET MVCジェネリクスによって強く型付けされたViewPageクラスは、上のようにモデルプロパティを直接扱えるので便利だ。

※OpenXMLがISO標準に認定されたこともあり、XMLスプレッドシート2003の今後には不安が残るのがこの方法の弱点だ。その場合は大人しくOpenXMLを使うべきなんだろうが、JSP同様、コンテンツをビューの雛形として使うのは楽ちんなんだよなぁ。(OpenXMLはパッケージを開けてみないと分らないしコピペもできない)

2007-01-03

[]テンプレート違い


先日のエントリでは上美谷さんにコメントを頂いた。テンプレート違いだが、紹介して頂いたmumurikさんの記事を読んでみる。

ITemplateでコントロールのプレゼンテーションを分離する(前編)

なるほど面白い。ITemplateを使うとASP.NETのネストされたコントロールを親のコントロールに注入してくれるんだ。これってまんまIoC(DI)じゃないか。

せっかくなので後編はWebControlの基礎を学んでから読ませていただこう。私はこの記事を読むにはまだ勉強不足だ。

2007-01-02

[]HTTPモジュールとHTTPハンドラ


ASP.NET の HTTP モジュールと HTTP ハンドラの概要
HTTP モジュールと HTTP ハンドラを使用してプラグ可能な ASP.NET コンポーネントを作成する

ふむふなるほど。HTTPモジュールはフロントコントローラパターンの実装。HTTPハンドラはServletフィルタと同じ用途か。HTTPモジュールはグローバルフィルタと考えたほうがいいのかな。
過去にISAPIエクステンションとISAPIフィルタを書いたことがあるが、ASP.NETでは同様のことをより簡単に書いて使うことができるのね。
そういやASP.NETを使ったテンプレートエンジンとか見たこと無いけど、現時点のPage/WebFormsアーキテクチャがあまりに強力なんで置き換える気も失せるのかね。

と思ったらNVelocityなんてあるんだ。なんだ更新止まってるのか、残念だなと思いきや
MonoRail / Castle project
ここで結実しているんだな。

2006-12-25

[][]名前空間エイリアス修飾子演算子(::演算子)


ASP.NETのWebFormデザイナが生成したソースコードを見てみると、そこには見慣れない(というか、WindowsFormsのコードビハインドでは見たことが無い)修飾子によりSystem.Web.UI名前空間のコンポーネントが定義されていた。

protected global::System.Web.UI.HtmlControls.HtmlForm form1;

protected global::ってなんだ? とhelpで"global"と入力しても、Google等で検索しても引っかからない。

すわ、まだ知らない記述子があったのかと思ったが、正体はこれだった。

:: 演算子
名前空間エイリアス修飾子演算子です。

名前空間エイリアス修飾子 (::) を使用して識別子を検索できます。この例に示すように、常に 2 つの識別子の間に配置します。
MSDN ライブラリィ C# リファレンス

コードの記述の中で、強制的にグローバル名前空間を参照する際に使用する記述方法だ。
解説によると

名前空間エイリアス修飾子として global を指定できます。これにより、エイリアスを使用した名前空間ではなく、グローバル名前空間で検索が実行されます。

とあるが、実際のところ"global"以外は使わないのだしこれ以外指定できないのにどうしてこういう書き方なんだろう。予約語のように後で"global"以外の別な修飾子が追加されるのだろうか。

追記:
青柳さんにコメント頂いた。エイリアス修飾子は別にglobalに限ったことではなく、自分が定義したエイリアスも記述できるのだそうだ。

それにしても、C++のスコープ解決演算子"::"と同じとはやられた。