この間の WebMatrix の管理ページを拡張する話、昨日たどりついた ASP.NET MVC で WebMatrix の管理ページを出す方法、どっちも ApplicationPart ってのが VirtualPath をアセンブリにマップして、アセンブリ内に置かれたページを出してるわけでございまして。
その _admin を出すようにした ASP.NET MVC アプリケーションのルートテーブルをダンプしても _admin へのルートなんて存在しないわけでありまして。
同様に WebMatrix のサイトでは Routing に “r.ashx/{module}/{*path}” しかないわけで。
どう考えてもなんか怪しいですよねってのが今回のネタ。
cshtmlのExecuteにブレークポイント貼ってみた
この間の WebMatrix の管理ページをカスタムしてみたページに System.Diagnostics.Debugger.Break() 仕込んで MVC アプリケーションに _admin 出す細工をしてカスタムアドミンページに飛び込んだ時のスタックトレースが以下。
スタックトレースを見ると2回の WebPageBase.ExecuteHierarcy の呼出しがある。これは _admin の内側(自分がカスタムした部分)と外側(レイアウト部)と考えられるのでWebPageHandlerのProcessReqest.AnonymouseMethod あたりから実際のページの処理になっていますねと。普通に IHttpHandler として実行されていますってだけしか解らず。これはルーティングが解決された後の話で Handler がnew される瞬間を捕まえてるわけじゃないんでルーティングがらみを追うとしては手遅れ
cshtml のctorにブレークポイント貼ってみた
じゃぁハンドラをnewしてる所をって事でコンストラクタにブレークを置いた結果が以下。

んで、これを元手にソースを codeplex から取ってきたソースを追ってみましょうって話。
まずスタックトレースを眺めて入口を探す
System.Web.HttpApplication.IExecutionStep.Execute までは実質 ASP.NET ランタイムが HttpApplication をドライブしてる所なんで無視して良いとして WebPageHttpModule.OnApplicationPostResolveRequestCache という事で、リクエストキャッシュの後処理の中でハンドラを new しているようですね。(System.Web.HttpApplication.PostResolveRequestCacheイベント)
Context.RemapHandler はハンドラマッピング=ルーティング
次の段階が WebPageRoute.OnPostResolveRequestCache ですね。はい、確かにここで Context.RemapHandler 呼んでるし、ハンドラの解決をしてるっぽいですね。
で、そのクラス内の MatchRequest が返した WebPageMatch で決まるページが暗黙にDisabledでなければ CreateFromVirtualPath してスタックトレースの通りに流れに飛び込みます。
って事は実質のルーティング作業は MatchRequest がやってますね。
MatchRequestは FileExists ってアセンブリ内のクラスだからファイル無いとか安心できずパラメータ名の virtualPathFactoryManager からして VirtualFile の可能性が怪しいんでと潜ってみると、VirtualPathFactoryManager.PageExistsに行ってやっぱりすぐ隣で VirtualPathProvider とか触ってるしで
if (_virtualPathFactories.Any(factory => factory.Exists(virtualPath)))
return true;
でnew List<IVirtualPathFactory>()のスキャンと。
IVirtualPathFactoryの向こう側
実装がインターフェースの向こう側に行ってしまったので闇の中って感じなんですが、ここでReSharperにFind Usages Advanced すると DictionaryBasedVirtualPathFactory しか実装無いよとおっしゃるので、本当ですかそうですか。確かにスタックトレースでも DictionaryBasedVirtualPathFactory.CreateInstance の中飛び込んでるんでそこに行ってるんですね。
ApplicationPartが居た
調子にのってReSharperで DictionaryBasedVirtualPathFactory をFind Usages Advanced すると

ビンゴで ApplicationPart につながったと。
登録側から追う
ApplicationPart.Register が ApplicationPartRegistory.Register に行って、ApplicationPartRegistory.Register が…
// Get all of the web page types
var webPageTypes = from type in applicationPart.Assembly.GetTypes()
where type.IsSubclassOf(_webPageType)
select type;
// Register each of page with the plan9
foreach (Type webPageType in webPageTypes) {
RegisterWebPage(applicationPart, webPageType, registerPageAction);
}
Assemblyの中のおいしそうなTypeを RegisterWebPage してて、RegisterWebPageが
// Register a page factory for it
_virtualPathFactory.RegisterPath(virtualPath, pageFactory);
_virtualPathFactoryに…で _virtualPathFactory が目的のDictionaryBasedVirtualPathFactoryですよっと。
private readonly DictionaryBasedVirtualPathFactory _virtualPathFactory
繋がった結果のまとめ
で、結果眺めると、やっぱり System.Web.Routing つかってねー!!!!
System.Web.Routing でのハンドラマッピングは PostResolveRequestCache だとどっちが先かっていう事ですが…UrlRoutingModule Class (System.Web.Routing) のメソッド PostResolveRequestCache、一緒かよ!
って訳でモジュールの登録順依存で Routing が勝つかもしれんし、ApplicationPartでのマッピングが勝つかもしれないねぇと。(後に MapRequestHandler した方が勝つ、いわゆる後勝ち)
で、どうなのよ。というのに答えてくれるのはやっぱりエース。 無聊を託つ: WebMatrixの認証設定とヘルパー の中ほどでModuleのリストしてるねっ!
はい、 ApplicationPart の勝利でございました。
まとめのまとめ
ReSharperとエース、これでまず間違いない。