<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-292244481221667867</atom:id><lastBuildDate>Sun, 12 Feb 2012 10:53:46 +0000</lastBuildDate><category>node.js</category><category>AOP</category><category>お知らせ</category><category>BoF</category><category>画像処理</category><category>AJAX</category><category>DataService</category><category>T4</category><category>Azure</category><category>ASP.NET MVC</category><category>開発</category><category>ASP.NET</category><category>MongoDB</category><category>TDD</category><category>Mesh</category><category>サルベージ</category><category>Unity</category><category>Mono</category><category>LINQ to SQL</category><category>Routing</category><category>Storage</category><category>TechDays</category><category>mvcConf</category><category>Gears</category><category>Blogsvc.net</category><category>jQuery</category><category>MySQL</category><category>V2</category><category>WebMatrix</category><category>xVal</category><category>CSS</category><category>Unit Test</category><category>DomainService</category><category>jQuery Mobile</category><category>ごにょごにょ</category><category>遊び</category><category>Oxite</category><category>IIS</category><category>ホッケー</category><category>読書</category><category>OpenID</category><category>DI</category><category>T4MVC</category><category>C#</category><category>テスト</category><category>Amazon AWS</category><category>Linux</category><category>Razor</category><category>EF</category><category>NHibernate</category><category>JavaScript</category><category>.NET</category><title>無聊を託つ</title><description /><link>http://takepara.blogspot.com/</link><managingEditor>noreply@blogger.com (たけはら)</managingEditor><generator>Blogger</generator><openSearch:totalResults>220</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/blogspot/jNRCtw" /><feedburner:info uri="blogspot/jnrctw" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-2494507587810121623</guid><pubDate>Sun, 12 Feb 2012 10:53:00 +0000</pubDate><atom:updated>2012-02-12T19:53:46.307+09:00</atom:updated><title>プロパティのバインドに失敗するときのエラーメッセージをカスタム</title><description>&lt;p&gt;&lt;a href="http://stackoverflow.com/"&gt;stackoverflow.com&lt;/a&gt;。 今でもたまに書き込んでます。&lt;/p&gt; &lt;p&gt;&lt;a href="http://stackoverflow.com/questions/8866907/regular-expression-stop-crashing"&gt;c# - Regular Expression stop crashing - Stack Overflow&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;a href="http://takepara.blogspot.com/2011/03/aspnet-mvcdataannotations.html"&gt;随分前にも似たようなガッカリ感&lt;/a&gt;を味わったんだけど、今回もまた似たような気分を味わってます。とはいえ、質問の真意を履き違えての結果の可能性は否めない。&lt;/p&gt; &lt;p&gt;この質問、必須項目にしたDateTime型のプロパティへのバインドに失敗するから、正規表現でチェックしたんだけど、”0000/00/00 00:00:00”って入力するとエラーになるんだよね。なんで？って言う感じでしょーか。&lt;/p&gt; &lt;p&gt;質問者のモデルを見るとPostedは必須項目。DateTimeには有効な日時しか入れられないっていう条件をあわせて考えると、不正な文字列の時にエラーメッセージを表示できれば要件は満たす。かな。正規表現がどうのこうのっていう問題解決の仕方ではなく、プロパティにセット出来なかった時のエラーメッセージがカスタム出来ればいいかもね、と思ったんですねー。&lt;/p&gt;&lt;pre class="prettyprint"&gt;private static string GetValueInvalidResource(ControllerContext controllerContext) {
	return GetUserResourceString(controllerContext, "PropertyValueInvalid") ?? MvcResources.DefaultModelBinder_ValueInvalid;
}

private static string GetValueRequiredResource(ControllerContext controllerContext) {
	return GetUserResourceString(controllerContext, "PropertyValueRequired") ?? MvcResources.DefaultModelBinder_ValueRequired;
}
&lt;/pre&gt;
&lt;p&gt;↑このようにMVCのDefaultModelBinderではそんな時のエラーメッセージをプロパティ”個別”にカスタムはできません。どんなプロパティにも共通のメッセージ。&lt;/p&gt;
&lt;p&gt;で考えた結果、投稿したコードが↓これ。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public class CustomModelBinder : DefaultModelBinder
{
  protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
  {
    base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);

    var propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
    var invalidMessage = propertyMetadata.AdditionalValues.ContainsKey("PropertyValueInvalid")
                 ? (string)propertyMetadata.AdditionalValues["PropertyValueInvalid"]
                 : string.Empty;
    if (string.IsNullOrEmpty(invalidMessage))
    {
      return;
    }

    // code from DefaultModelBinder
    string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
    if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey))
    {
      return;
    }
    ModelState modelState = bindingContext.ModelState[fullPropertyKey];
    foreach (ModelError error in modelState.Errors.Where(err =&amp;gt; String.IsNullOrEmpty(err.ErrorMessage) &amp;amp;&amp;amp; err.Exception != null).ToList())
    {
      for (Exception exception = error.Exception; exception != null; exception = exception.InnerException)
      {
        if (exception is FormatException)
        {
          string displayName = propertyMetadata.GetDisplayName();
          string errorMessageTemplate = invalidMessage;
          string errorMessage = String.Format(CultureInfo.CurrentCulture, errorMessageTemplate,
                            modelState.Value.AttemptedValue, displayName);
          modelState.Errors.Remove(error);
          modelState.Errors.Add(errorMessage);
          break;
        }
      }
    }
  }
}&lt;/pre&gt;
&lt;p&gt;これを利用するためにモデル定義を以下のように変えてしまいましょう。&lt;/p&gt;&lt;pre class="prettyprint"&gt;[Required]
[AdditionalMetadata(
 "PropertyValueInvalid",
 "Wrong Syntax Entered, Needed:day/Month/Year Hour:Minutes:Seconds")]
public DateTime? Posted { get; set; }&lt;/pre&gt;
&lt;p&gt;AdditionalMetadataにカスタムメッセージを指定して、プロパティのバインドがエラーになったらそのメッセージを表示する。素敵だと思うんだけどなー。&lt;/p&gt;
&lt;p&gt;で、コメントが「this code is like giving a baby a gun」。ぎゃふん。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-2494507587810121623?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/xWKnRSmoq_w" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/xWKnRSmoq_w/blog-post.html</link><author>noreply@blogger.com (たけはら)</author><feedburner:origLink>http://takepara.blogspot.com/2012/02/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-3349242369547032106</guid><pubDate>Sat, 28 Jan 2012 15:55:00 +0000</pubDate><atom:updated>2012-01-29T00:59:31.183+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">IIS</category><title>効率のいいリクエスト処理</title><description>&lt;p&gt;Webサイトの運用、大変ですよね。お疲れ様デスよ。まったく。&lt;/p&gt; &lt;p&gt;普通にネットワーク構成するとハードウェアロードバランサの下にWebサーバー並べて負荷分散する感じになるでしょう。処理が追いつかなかったらWebサーバー追加だぜ！とか、富豪富豪。ふぇっふぇっふぇ。でも、そんな富豪ばかりじゃない。&lt;/p&gt; &lt;p&gt;&lt;a href="http://technet.microsoft.com/ja-jp/iis/ee839425"&gt;Application Request Routing | IIS 拡張機能 | TechNet&lt;/a&gt; &lt;/p&gt; &lt;p&gt;略してARR。NLBみたいにネットワークの負荷分散じゃなくてHTTPでのリクエスト負荷分散。ソフトウェアLB。&lt;/p&gt; &lt;p&gt;ARRが単なるリバースプロキシだと思ったら大間違いでした。この子かなり優秀。&lt;/p&gt; &lt;p&gt;そもそも、SSLオフロードさせようと思って、パフォーマンステストをしてたんですけど、思いの外Request / Secが高くなる。変だなーと思って、HTTPSじゃなくてHTTPでも試してみたら、それでもRequest / Secが高い。なんで！？と思いパフォーマンスモニターでいろいろ見てたら、どーもノードサーバーの処理が重い時にはARRがリクエストのルーティングをちょっと保留してるくさい。レスポンスタイムを見て、重くなってきたのかどーかを判断してるのかなー。それ以外に指標がないと思うので、そーじゃないかという予想。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-ywsL_gl2TIY/TyQaByYpFQI/AAAAAAAACbU/zM6UeEd9omk/s1600-h/arr1%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr1" border="0" alt="arr1" src="http://lh6.ggpht.com/-w18m1F2Oiaw/TyQaChfjjuI/AAAAAAAACbY/skEXWc7dUy0/arr1_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-Th7y9h-6It4/TyQaDT-U_0I/AAAAAAAACbg/8meLyY-ng_4/s1600-h/arr2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr2" border="0" alt="arr2" src="http://lh6.ggpht.com/-c7ijP5w5k6I/TyQaDwfOjnI/AAAAAAAACbo/pFuXsH97pas/arr2_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;VS のロードテスト(今はまだUltimate使えるからね)でチェックしてみました。１０～２５０ユーザーまで徐々に増やしていく設定でWebアプリケーションの動きをシミュレートさせる。下のグラフはパフォーマンスモニタ。&lt;/p&gt; &lt;p&gt;黄色がCPU、青がWeb Service：Current Connections、緑がW3SVC_W3WP：Request / Sec、赤がASP.NET v2.0：Request Queued。&lt;/p&gt; &lt;p&gt;まずは、ARRを通さずに直接リクエストした状態です。CPUは概ねフルに使いつつ、コネクション数が段階的に増え、リクエストキューに処理待ちリクエストが溜まっていく様子です。直感的にそーなるなー、っていう動きがそのまま再現できてますね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-epu_U3wuFq0/TyQaE1eCm1I/AAAAAAAACbw/naxRJkjO8wc/s1600-h/arr3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr3" border="0" alt="arr3" src="http://lh5.ggpht.com/-f0CZ9rRFnuw/TyQaFUN7mVI/AAAAAAAACb4/c7bgkXrlqak/arr3_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;どんどん負荷を上げる(ユーザー数を増やす)と、キューにたまる数が増えて、グラフ上Request / Secを上回ります。これはつまり同時処理性能を超えたリクエストがWebアプリケーションサーバーに来てるっていう状態ですね。さばけないからキューイング。ASP.NETのキューイング。&lt;/p&gt; &lt;p&gt;最終的にすべて終了すると↓こうなって、何もかも0に戻る。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-cDsHAblM1WY/TyQaGP4GegI/AAAAAAAACcA/R2-p205AHzg/s1600-h/arr4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr4" border="0" alt="arr4" src="http://lh5.ggpht.com/-CKYAnpDWaPU/TyQaHPxQi_I/AAAAAAAACcM/201lTYtfBtU/arr4_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ロードテスト結果。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-iF0Pd-_QElg/TyQaHzznWdI/AAAAAAAACcU/icL29o-QvS8/s1600-h/arr5%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr5" border="0" alt="arr5" src="http://lh5.ggpht.com/-4gHMPOlMMMw/TyQaIn2yRjI/AAAAAAAACcc/IeTl8gL6AIE/arr5_thumb%25255B1%25255D.png?imgmax=800" width="240" height="149"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Request / Secが281でページ/秒の平均が26.0です。&lt;/p&gt;   &lt;p&gt;今度は全く同じ事をARR経由させて実行します。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-I-P-eM4nFek/TyQaKFXIEAI/AAAAAAAACck/ERtm4A7xIJk/s1600-h/arr6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr6" border="0" alt="arr6" src="http://lh4.ggpht.com/-ryATg6-O8r0/TyQaK0pAinI/AAAAAAAACcs/rLhQKHdA4f8/arr6_thumb.png?imgmax=800" width="240" height="149"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-eQ0G6sW5Uno/TyQaL6ZT1EI/AAAAAAAACcw/1oIvVRsph5M/s1600-h/arr7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr7" border="0" alt="arr7" src="http://lh3.ggpht.com/-Z_CseCg2mGg/TyQaMXaJ5qI/AAAAAAAACc4/gL4dykH9BNQ/arr7_thumb.png?imgmax=800" width="240" height="176"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;この段階ですでに挙動が違うんですよね。下のグラフはWebアプリケーションサーバーの状態なんですけど、コネクション数の増え方が違う。段階的なんだけど、伸びかたが明らかに違う。この時のARRの同じグラフを見てみるとどーなってるか。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-VR6J4AXAJEs/TyQaNAHlFsI/AAAAAAAACdA/gzo88GZfJX4/s1600-h/arr8%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr8" border="0" alt="arr8" src="http://lh5.ggpht.com/-rn9Sxh6PVFI/TyQaNzOpUoI/AAAAAAAACdI/AWNMZCLz-Rg/arr8_thumb.png?imgmax=800" width="240" height="172"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;なるほどね。こっちにはユーザー数と同じ増え幅でコネクション数が増えてます。&lt;/p&gt; &lt;p&gt;ARRからノードサーバー(Webアプリケーションサーバー)へはTCP接続を使いまわしてる？コネクションプーリング。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-XnWQE8VV8xo/TyQaOkjsrkI/AAAAAAAACdQ/Q5xUTyN_njs/s1600-h/arr9%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr9" border="0" alt="arr9" src="http://lh6.ggpht.com/-hRu-Q7nbg8U/TyQaPeO0T6I/AAAAAAAACdY/jwDys77zB7g/arr9_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ユーザー数をどんどん増やしていくと、リクエストキューにたまるのが直接接続時の挙動でしたが、ARRを経由した場合、キュー(赤)がたまるのに合わせてコネクション数(青)が増えてる。関連があるような挙動ですね。&lt;/p&gt; &lt;p&gt;で、観測を続けると、キューの数が減少に転じてます。不思議ですね。ユーザー数は同じでリクエスト数も同じように発生してるのに、Webアプリケーションサーバーではキューがたまりません。&lt;/p&gt; &lt;p&gt;その時のロードテストの状態。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-0cJBV9OS7Z8/TyQaQVojXVI/AAAAAAAACdk/Jj7obXXkYVA/s1600-h/arr10%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr10" border="0" alt="arr10" src="http://lh5.ggpht.com/-E-8L6tf3yFI/TyQaQ4KFXhI/AAAAAAAACds/_Rmy-WWn5Aw/arr10_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Request / Secは298で落ちてない。若干上がってるくらい。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-N_VaY4GCpLY/TyQaRxkGSeI/AAAAAAAACdw/cAsuAHBGxGM/s1600-h/arr11%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr11" border="0" alt="arr11" src="http://lh3.ggpht.com/-AEcLHV5d4IA/TyQaSYnJpoI/AAAAAAAACd4/7b621UcwUl4/arr11_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;最終的にテスト実行完了時点でCPUやRequest/Sec、キューの数は0に落ちるのに、コネクション数(青)はまだ0にならない。しばらく高止まり。これはつまりARRがコネクションをすぐに切ってないということですね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-LdoruFhS62U/TyQaTFNd1qI/AAAAAAAACeE/VyI3D8VwVPw/s1600-h/arr12%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="arr12" border="0" alt="arr12" src="http://lh3.ggpht.com/-y-SzBVbYsF8/TyQaUydbgvI/AAAAAAAACeM/yMbQPwwVSO0/arr12_thumb.png?imgmax=800" width="240" height="148"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Request / Secが347でページ/秒の平均が32.4.0。ページの応答時間平均にいたっては6.45から4.20ですよ！約1.5倍。これはどういうことなのかというと、Webアプリケーションサーバーの同時処理性能が下がるようなリクエストをルーティングせず、しばらくARRでキューイングしておくことで、Webアプリケーションサーバーがベストパフォーマンスを出せるように調整してるから、じゃないでしょうか。実際にWebアプリケーションサーバーのキューの数はARRを経由させたほうが少ない状態を維持し、コネクション数も少ない。何度か試した結果コネクション数が半分の時もあるのに、ページの応答時間平均は常に概ね1.5倍早い。&lt;/p&gt;   &lt;p&gt;無理に同時実行させないことで早くレスポンスを返す。結果的にRequest/Secも上がる。SSLオフロードでWebアプリケーションサーバーのCPU利用を実処理に専念させようとしただけなのに、この結果。恐るべしARR！&lt;/p&gt; &lt;p&gt;ちなみにARRとWebアプリケーションサーバーが同居した場合にはまるで性能向上しませんでした。してもいいかと思ったんだけど、そういう訳でもないんですね。IISとASP.NETの双方でのCPU利用(ネットワークも思いのほかCPU利用するのかもね)を最適化するならARRは別サーバーで用意しましょう。&lt;/p&gt; &lt;p&gt;ARR賢い。単なるリバースプロキシじゃない。ノードサーバーはIISに限定するものじゃないので、性能判断の指標はレスポンスタイムしか無いと思うけど、その辺の情報は見つけることが出来ませんでした。&lt;/p&gt; &lt;p&gt;今回はARR1台、ノードサーバー(Webアプリケーションサーバー)1台なので、こういう結果でしたが、ノードサーバーの台数が増えた場合にはどのように効率化されるのかは、環境次第だと思うので、自身の環境で試してみたらどーでしょー。&lt;/p&gt; &lt;p&gt;もし、仮に同じような性能向上をするならWebアプリケーションサーバーが4台以上ある場合、1台をARRにしたほうがレスポンス性能がいいということになるかもよー。&lt;/p&gt; &lt;p&gt;目指せ、続ハイパフォーマンスWebサイト！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-3349242369547032106?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/WQcos95_iyo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/WQcos95_iyo/blog-post.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-w18m1F2Oiaw/TyQaChfjjuI/AAAAAAAACbY/skEXWc7dUy0/s72-c/arr1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2012/01/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-494696489443622633</guid><pubDate>Mon, 02 Jan 2012 10:29:00 +0000</pubDate><atom:updated>2012-01-02T19:41:50.669+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">テスト</category><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">開発</category><title>HttpBench on GitHub</title><description>&lt;p&gt;年明け早々です！お正月生まれのツール。&lt;/p&gt; &lt;p&gt;&lt;a href="https://github.com/takepara/HttpBench"&gt;HttpBench – GitHub&lt;/a&gt; &lt;/p&gt; &lt;p&gt;ApacheBenchにはいつもお世話になってます。でも、同じようにHTTPSも計測したいです。absめ！&lt;/p&gt; &lt;p&gt;&lt;strong&gt;localhostに対してab –n 100 –c 10 http://localhost/&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-KtLbSlQwECM/TwGHAbZFViI/AAAAAAAACZ8/93cDeCvLo-s/s1600-h/hb3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="hb3" border="0" alt="hb3" src="http://lh6.ggpht.com/-m7O2P-i5MJ8/TwGHBKlsd0I/AAAAAAAACaE/TwyutqSKHt4/hb3_thumb.png?imgmax=800" width="240" height="205"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;localhostに対してhb –n 100 –c 10 http://localhost&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-s0q5VSa7a_8/TwGHBzU4sKI/AAAAAAAACaM/LdCv90fGzLs/s1600-h/hb4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="hb4" border="0" alt="hb4" src="http://lh5.ggpht.com/-TirAWGVjL4Y/TwGHCl4lxBI/AAAAAAAACaU/sUXTgi6okeA/hb4_thumb.png?imgmax=800" width="240" height="205"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;それっぽいー。けど、手抜きすぎー。&lt;/p&gt; &lt;p&gt;せっかくなのでIIS ExpressでHTTPSへのリクエストを確認。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-HDKgRujcquM/TwGHDcbVEZI/AAAAAAAACac/m7_ZtnyUv-o/s1600-h/hb5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="hb5" border="0" alt="hb5" src="http://lh5.ggpht.com/-dBTUISQQ8OU/TwGHEVG2BDI/AAAAAAAACak/A_18WdCrmg0/hb5_thumb.png?imgmax=800" width="240" height="205"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh4.ggpht.com/-yi4nIW_YQ1U/TwGHFbVRj7I/AAAAAAAACas/eLq49oiKg6s/s1600-h/hb6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="hb6" border="0" alt="hb6" src="http://lh4.ggpht.com/-zbm5jqermhg/TwGHGYxlE8I/AAAAAAAACa0/Vm0iyHYsv2s/hb6_thumb.png?imgmax=800" width="240" height="205"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;左がHTTPで右がHTTPS(IIS Expressの勝手証明書)。ab(abs)だとうまくできないですよね。&lt;/p&gt; &lt;p&gt;abだと、すごくたくさんオプション指定できるし、計測も細かい。流石です。が、hbはまだ全然。とりあえず実行回数/コンカレント数/BASIC認証、HTTP GETだけ。&lt;/p&gt; &lt;p&gt;あ。バイナリ配布ってどうしよ．．．。いつかすると思う。今はそんな段階でもないからいいよね。&lt;/p&gt; &lt;p&gt;コンカレントを&lt;a href="http://msdn.microsoft.com/ja-jp/library/system.threading.tasks.paralleloptions.maxdegreeofparallelism.aspx"&gt;MaxDegreeOfParallelism&lt;/a&gt;じゃなくて、Threadを作って処理してます。普通にTaskでもよかったのかなー。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-3Z9iQXXpJVQ/TwGHHWeGNoI/AAAAAAAACa4/0ahF_ur_sK0/s1600-h/hb7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="hb7" border="0" alt="hb7" src="http://lh5.ggpht.com/-FyZqL0GWcfw/TwGHHwkMO4I/AAAAAAAACbA/29Ec6loQKww/hb7_thumb.png?imgmax=800" width="240" height="124"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;↑-c 200指定。&lt;/p&gt; &lt;p&gt;目指せハイパフォーマンスWebサイト！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-494696489443622633?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/b814OWgRZFo" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/b814OWgRZFo/httpbench-on-github.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-m7O2P-i5MJ8/TwGHBKlsd0I/AAAAAAAACaE/TwyutqSKHt4/s72-c/hb3_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2012/01/httpbench-on-github.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-771043258811180036</guid><pubDate>Sat, 31 Dec 2011 10:47:00 +0000</pubDate><atom:updated>2011-12-31T19:47:11.796+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>MongoDBでお気楽MapReduce</title><description>&lt;p&gt;MongoDBにはMapReduceの仕組みが用意されてて楽しそうですよね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/display/DOCSJP/MapReduce"&gt;MapReduce - Docs-Japanese - 10gen Confluence&lt;/a&gt; &lt;/p&gt; &lt;p&gt;ちゃんと日本語。&lt;/p&gt; &lt;p&gt;で、コンソールで入力しながらの開発は面倒なので、こんな時はMongo VUE。安くならないかなー。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongovue.com/2010/11/03/yet-another-mongodb-map-reduce-tutorial/"&gt;Yet another MongoDB Map Reduce tutorial | MongoVUE&lt;/a&gt; &lt;/p&gt; &lt;p&gt;使い方、これじゃよく分からない。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongovue.com/2011/04/05/how-to-perform-mapreduce-operations-in-mongovue-a-step-by-step-guide/"&gt;How to perform MapReduce operations in MongoVUE | MongoVUE&lt;/a&gt; &lt;/p&gt; &lt;p&gt;こっちですね！この手順どおりに実行すれば、ちゃんとMapReduce！Sharding環境ならそれぞれのShardで分散処理してくれます。&lt;/p&gt; &lt;p&gt;せっかくなので、以前作った&lt;a href="http://takepara.blogspot.com/2011/10/tracelistner-into-mongodb.html"&gt;MongoTraceListner&lt;/a&gt;のサンプルを利用して、各URLの処理時間の平均をMapReduceで計算してみます。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public class PerformanceTraceModule : IHttpModule
{
  private string ItemKey = "_mongoDbTraceStart";

  public PerformanceTraceModule()
  {
  }

  public void Init(HttpApplication context)
  {
      context.BeginRequest += new EventHandler(context_BeginRequest);
      context.EndRequest += new EventHandler(context_EndRequest);
  }

  void context_BeginRequest(object sender, EventArgs e)
  {
      var httpContext = (sender as HttpApplication).Context;
      var startTime = httpContext.Items[ItemKey] = DateTime.Now;
  }

  void context_EndRequest(object sender, EventArgs e)
  {
      var httpContext = (sender as HttpApplication).Context;
      var startTime = (DateTime)httpContext.Items[ItemKey];

      Trace.WriteLine(new
      {
          Method = httpContext.Request.HttpMethod,
          Status = httpContext.Response.StatusCode,
          RawUrl = httpContext.Request.Url.ToString(),
          Milliseconds = (DateTime.Now - startTime).Milliseconds
      });
  }

  public void Dispose()
  {
  }
}
&lt;/pre&gt;
&lt;p&gt;このHttpModuleを仕掛けて処理時間をMongoDBに入れます。&lt;/p&gt;
&lt;p&gt;実行！何度も何度もリロードやら、About、Loginへのアクセスを繰り返す。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-w4R0TOebupQ/Tv7n7p0xHlI/AAAAAAAACXs/NSEWb92D5Ss/s1600-h/mr2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr2" border="0" alt="mr2" src="http://lh3.ggpht.com/-EFMbo0KiR7o/Tv7n8V7zO2I/AAAAAAAACXw/SwI7VnJwrjM/mr2_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;データも溜まって来ました。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-8fisJlrH4rQ/Tv7n85FSR3I/AAAAAAAACX8/bmtfKaAKhjI/s1600-h/mr1%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr1" border="0" alt="mr1" src="http://lh4.ggpht.com/-zdW2TMQNS28/Tv7n-A-tSXI/AAAAAAAACYA/tlPCgVUBjLs/mr1_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ここで、MapReduceを実行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Map&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-72RhvCGrNbk/Tv7n-uXgsDI/AAAAAAAACYM/CQwNFFYxDoo/s1600-h/mr3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr3" border="0" alt="mr3" src="http://lh6.ggpht.com/-WQzsGlroXkE/Tv7n_sp5dMI/AAAAAAAACYQ/_g5XFc8ZCJA/mr3_thumb.png?imgmax=800" width="240" height="213"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reduce&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/--hPsjgB2sLI/Tv7oAS9F3UI/AAAAAAAACYY/QAHeelqp528/s1600-h/mr4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr4" border="0" alt="mr4" src="http://lh3.ggpht.com/-hzZqDyqC7l0/Tv7oBLoo8JI/AAAAAAAACYg/mUv5PsVpIEQ/mr4_thumb.png?imgmax=800" width="240" height="213"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finalize&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-RXnZIqyw5Dc/Tv7oCPUjRDI/AAAAAAAACYs/P203ZSmoecE/s1600-h/mr5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr5" border="0" alt="mr5" src="http://lh3.ggpht.com/-WY9-eVCiilU/Tv7oEJq8GYI/AAAAAAAACY0/bTnnwl2Q_Yo/mr5_thumb.png?imgmax=800" width="240" height="213"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In &amp;amp; Out&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-xRkUM-sXj7U/Tv7oEv-_5-I/AAAAAAAACY4/8x9wjXgxSuY/s1600-h/mr6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr6" border="0" alt="mr6" src="http://lh3.ggpht.com/-05nhy2R3Vlc/Tv7oFf6T30I/AAAAAAAACZE/APB_c-6VYM0/mr6_thumb.png?imgmax=800" width="240" height="213"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;結果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-TXzWWqxSb-U/Tv7oGa1SeTI/AAAAAAAACZM/22TgCWaV83c/s1600-h/mr7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr7" border="0" alt="mr7" src="http://lh3.ggpht.com/-HIJN5g3k9CU/Tv7oHAx0_RI/AAAAAAAACZQ/GxOAMxC4bdw/mr7_thumb.png?imgmax=800" width="240" height="213"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ただ、ここでmap/reduce/finalizeのコードに間違いがあっても、わかりにくい。どこのなにがエラーなのかサッパリ。そんな時はコンソールを見てみましょう。サービス化してたらログファイル。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-fDixoR7Utbc/Tv7oH0bmKBI/AAAAAAAACZc/iesimDguP_0/s1600-h/mr8%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr8" border="0" alt="mr8" src="http://lh5.ggpht.com/-dAg_WM32PdI/Tv7oIrxIbOI/AAAAAAAACZg/tOBey7hw0DI/mr8_thumb.png?imgmax=800" width="240" height="214"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;finalizeで適当にエラーコードを入れて実行するとコンソールにエラーメッセージが出てます。開発中はMongoDB Shellでやるか、ローカル実行してコンソール確認しながらすすめるのがいいかもー。&lt;/p&gt;
&lt;p&gt;Mongo VUEやShellで見るだけじゃなくて、ASP.NET MVCのアプリケーションから見れるようにするために、以下のようにしてみました。&lt;/p&gt;&lt;pre class="prettyprint"&gt;private MongoDatabase GetDatabase()
{
  var serverName = ConfigurationManager.AppSettings["MongoDb:Server"];
  var databaseName = ConfigurationManager.AppSettings["MongoDb:Database"];

  if (string.IsNullOrEmpty(serverName))
      serverName = "mongodb://localhost";

  if (string.IsNullOrEmpty(databaseName))
      databaseName = "TraceListner";

  var server = MongoServer.Create(serverName);
  return server.GetDatabase(databaseName);
}

private BsonJavaScript GetMapReduceCode(string name)
{
  return new BsonJavaScript(System.IO.File.ReadAllText(Server.MapPath("~/MapReduce/" + name)));
}

public ActionResult MapReduce()
{
  var map = GetMapReduceCode("map.js");
  var reduce = GetMapReduceCode("reduce.js");
  var finalize = GetMapReduceCode("finalize.js");

  var collection = GetDatabase().GetCollection("MyTrace");
  var options = new MapReduceOptionsBuilder();
  options.SetFinalize(finalize);
  options.SetSortOrder("_id");
  options.SetOutput(MapReduceOutput.Inline);

  var result = from r in collection.MapReduce(map, reduce, options).GetResults()
               select new
               {
                  RawUrl = (string)r["_id"],
                  Count = r["value"].AsBsonDocument["Count"].ToInt32(),
                  Elapsed = r["value"].AsBsonDocument["Elapsed"].ToInt32(),
                  Average = r["value"].AsBsonDocument["Average"].ToDouble()
               };
  return Json(result.ToArray().OrderByDescending(mr=&amp;gt;mr.Average), JsonRequestBehavior.AllowGet);
}&lt;/pre&gt;
&lt;p&gt;HomeControllerに追加。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-TOeWr1S1H2Q/Tv7oKyhH_gI/AAAAAAAACZs/xUBRd0VVQc0/s1600-h/mr9%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mr9" border="0" alt="mr9" src="http://lh6.ggpht.com/-TCewPFONxXY/Tv7oLpJxNVI/AAAAAAAACZw/wbF7nQYR5NE/mr9_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;やったー。出たー。あとは、見た目よろしくしていけばOKでしょう。今回は気にしないけど。&lt;/p&gt;
&lt;p&gt;こんな感じでMongoDBにデータ入れとけばMapReduceで集計をMongoDBサーバーに任せられていいですね！&lt;/p&gt;
&lt;p&gt;っていう、結論じゃなくて、結局はV8でスクリプト動かしてるから、大量のデータの場合そんなに早くなかったりしてショック。対象データをすべてフェッチしてC#で処理したほうが早かったりも．．．。&lt;/p&gt;
&lt;p&gt;速度的には思ったほどじゃないけど、これはこれで面白いので、PowerPivotとあわせて業務系の人が扱えるようになると、嬉しいですよねー。&lt;/p&gt;
&lt;p&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; padding-right: 0px; padding-top: 0px" title="Preview" height="120" marginheight="0" src="https://skydrive.live.com/embed?cid=DF0C51DB6AD31F29&amp;amp;resid=DF0C51DB6AD31F29%214734&amp;amp;authkey=AKIFQGFVAcW-0JE" frameborder="0" width="98" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-771043258811180036?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/6eSYO0acdtc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/6eSYO0acdtc/mongodbmapreduce.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-EFMbo0KiR7o/Tv7n8V7zO2I/AAAAAAAACXw/SwI7VnJwrjM/s72-c/mr2_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/12/mongodbmapreduce.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-1191731882675718984</guid><pubDate>Fri, 30 Dec 2011 16:25:00 +0000</pubDate><atom:updated>2011-12-31T01:39:41.862+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Amazon AWS</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><title>URLRewrite＋CloudFrontでパフォーマンスを取り戻す</title><description>&lt;p&gt;年末ですね。年末だからこそ大量のアクセスが発生することもありますね。コンシューマー向けのものだと、平時に比べれば多かったりしますよね。爆発的なアクセスになることもありましょう。おめーさん、容赦無いね！&lt;/p&gt; &lt;p&gt;特にHTTPS。コレはもうホントしんどいですよね。SSLアクセラレータとかSSLオフロードっていうか、そういう前段でさばくように構成して、実処理はHTTPのみにしとかないとHTTPSの処理に尋常じゃないCPU使用率を持っていかれたりしちゃいましょう。そーなると、本来の処理にCPUが割り当てられない。SSLハンドシェーク。悪魔のようです。でも、そんな構成すぐに取れない！そんな時の選択肢としてCDN。え？なんで？だってアクセスの総量が減ればその分処理量も減じゃない！コードチューニングより効果が高いこともあります。パフォーマンス20％アップのコードチューニングより200％アップ(することもある)のCDN。&lt;/p&gt; &lt;p&gt;お手軽なのはAmazon CloudFront。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;カスタムオリジンで既存サーバーをそのまま利用することで、オリジンサーバーにコンテンツを事前にアップロードしなくてもいい  &lt;li&gt;URLRewrite2.0から導入されてるOutboundRulesでのHTML書き換えで、CDNを指すようにしてしまうことで、レンダリングコードに(ほとんど)手を加えずにCDNが使えるようになる  &lt;li&gt;CNAMEでカスタムドメインでもそれなりに(HTTPSのカスタムドメインはダメだけど)  &lt;li&gt;コンテンツへのHTTPSアクセス負荷をCloudFrontに肩代わりさせれる(コレ！！)  &lt;li&gt;使ったぶんだけ課金は、いまさらですけどやっぱり素敵ポイント&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;もちろんIISね。Apacheもなんか書き換えありましたよね。&lt;a href="http://httpd.apache.org/docs/2.2/ja/mod/mod_ext_filter.html"&gt;mod_ext_filter&lt;/a&gt;？IIS以外はよく知らないです。&lt;/p&gt; &lt;p&gt;ココはひとつ、mvcPhotos(覚えてますか？)に実験台として登場してもらいましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-Shl8O_lqr7c/Tv3l1SXgcjI/AAAAAAAACV8/m8hQc2--bcg/s1600-h/cf2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf2" border="0" alt="cf2" src="http://lh4.ggpht.com/-w0T3Fi82kB8/Tv3l2HsON3I/AAAAAAAACWA/tKBt7FAFBh4/cf2_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://mvcphotos.takepara.com"&gt;http://mvcphotos.takepara.com&lt;/a&gt;&lt;/p&gt; &lt;p&gt;まだ見れる状態になったままでした。そろそろ閉鎖しないと．．．。&lt;/p&gt; &lt;p&gt;&lt;a href="http://mvcphotos.codeplex.com/"&gt;http://mvcphotos.codeplex.com/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;CloudFrontの登録とか、そういうのはいろんな人が書いてるので、その辺は省略。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-aU6RcAVZsSI/Tv3l22ncofI/AAAAAAAACWM/1Z1r_lrX94M/s1600-h/cf3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf3" border="0" alt="cf3" src="http://lh3.ggpht.com/-kotcg0vPOmU/Tv3l37n2XLI/AAAAAAAACWQ/MvVof6Pklx0/cf3_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;こんな感じですね。コレといって何のへんてつもない設定。カスタムオリジンとProtocol PolicyをHttp Only、CNAMEでカスタムドメイン(HTTPSの時は割り当てられたドメインをそのまま使う)。&lt;/p&gt; &lt;p&gt;あとは、Web.configにoutboundRulesを追加(ExpressWebでURLRewrite使えると書かれてるけど、バージョンが見当たらなかったから不安だったけど、ちゃんと2.0以降が入ってる模様)。&lt;/p&gt;&lt;pre class="prettyprint"&gt;    &amp;lt;rewrite&amp;gt;
      &amp;lt;outboundRules&amp;gt;
        &amp;lt;rule name="CloudFrontContents" preCondition="html" enabled="true"&amp;gt;
          &amp;lt;match filterByTags="A,Img" pattern="^/Photos/Image/(.*)"/&amp;gt;
          &amp;lt;action type="Rewrite" value="//cdn.mvcphotos.takepara.com/Photos/Image/{R:1}"/&amp;gt;
        &amp;lt;/rule&amp;gt;
        &amp;lt;preConditions&amp;gt;
          &amp;lt;preCondition name="html"&amp;gt;
            &amp;lt;add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html"/&amp;gt;
            &amp;lt;add input="{REQUEST_URI}" pattern="/mobile" negate="true"/&amp;gt;
          &amp;lt;/preCondition&amp;gt;
        &amp;lt;/preConditions&amp;gt;
      &amp;lt;/outboundRules&amp;gt;
    &amp;lt;/rewrite&amp;gt;
&lt;/pre&gt;
&lt;p&gt;これだけ。これで、/mobile以外の時に/Photos/Imageへのアクセス(a hrefとimg src)すべてCDNへ変更します(詳しくは&lt;a href="http://learn.iis.net/page.aspx/657/creating-outbound-rules-for-url-rewrite-module/"&gt;Creating Outbound Rules for URL Rewrite Module : URL Rewrite Module 2 : URL Rewrite Module : The Official Microsoft IIS Site&lt;/a&gt; )。/mobileを除外してるのには理由があります。mvcPhotosはHTTPSでのアクセスが無いので意味ないんですが、実際は有りましょう。その際、ケータイからのアクセスだとCloudFrontで使ってるワイルドカード証明書が残念なコトになります。まだまだ正常に処理できないですよね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-HkNRLCmv2vI/Tv3l4miKbfI/AAAAAAAACWc/FlHgVgGj1Cg/s1600-h/cf%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf" border="0" alt="cf" src="http://lh5.ggpht.com/-YutDAc-rZGg/Tv3l5xlwlcI/AAAAAAAACWk/oXa9lEL1OsI/cf_thumb.png?imgmax=800" width="239" height="240"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なので、もともと素材の少ないケータイアクセスの場合(/mobile配下へのアクセス)は、CDNを利用しないようにして、コレまで通りのアクセスにしときます。PCやスマフォでのHTTPアクセスなら処理が負担にならない、って言う場合はconditionsを以下のように追加してHTTPSの時だけCDN参照するようにするのがいいでしょう。&lt;/p&gt;&lt;pre class="prettyprint"&gt;        &amp;lt;rule name="CloudFrontContents" preCondition="html" enabled="true"&amp;gt;
          &amp;lt;match filterByTags="A,Img" pattern="^/Photos/Image/(.*)"/&amp;gt;
          &amp;lt;action type="Rewrite" value="//cdn.mvcphotos.takepara.com/Photos/Image/{R:1}"/&amp;gt;
          &amp;lt;conditions&amp;gt;
            &amp;lt;add input="{HTTPS}" pattern="^on$"/&amp;gt;
          &amp;lt;/conditions&amp;gt;
        &amp;lt;/rule&amp;gt;
&lt;/pre&gt;
&lt;p&gt;ただ今回、mvcPhotosのちょっと残念だったところと、Cloud Frontの制限が丁度マッチして、ちょびっとだけViewとスクリプトの変更がありました。&lt;/p&gt;
&lt;p&gt;画像を返す部分をPhotosControllerにやらせてるんですが、そのパラメータをRoutingじゃなくてQueryStringで渡してたんです。が、Cloud FrontはQueryString無視します。なので、そこだけ変更してQueryStringでのサイズと変換方法の指定をRoutingパラメータにしました。&lt;/p&gt;
&lt;p&gt;/Photos/Image/1?size=100&amp;amp;type=fit&lt;/p&gt;
&lt;p&gt;↑こうだったものを↓こう。&lt;/p&gt;
&lt;p&gt;/Photos/Image/1/100/fit&lt;/p&gt;
&lt;p&gt;そのためにRoute登録。&lt;/p&gt;&lt;pre class="prettyprint"&gt;    routes.MapRoute(
        "PhotoImage",
        "Photos/Image/{id}/{size}/{type}",
        new {controller = "Photos", action = "Image"});&lt;/pre&gt;
&lt;p&gt;コードいじってるじゃん！さーせん。でも、普通はいじる必要ないはず。あっても、ViewやScriptだけで済むはずです。サービスとして公開するならURLの設計もちゃんとするはずなので。今回の微調整もRouting以外はViewだけ。View内にknockoutで使うHtml Template埋め込んでるし、パス関連は全てそのテンプレートに展開してるから、OutboundRulesの対象になる。&lt;/p&gt;
&lt;p&gt;これで、&lt;a href="http://mvcphotos.takepara.com/"&gt;mvcPhotos&lt;/a&gt;にアクセスしてみましょう。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-kRRRVmyq59E/Tv3l7XG3zWI/AAAAAAAACWs/QwgC6vob44g/s1600-h/cf4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf4" border="0" alt="cf4" src="http://lh4.ggpht.com/-yd2tfjwulHI/Tv3l8K6iUCI/AAAAAAAACW0/tPBJNphhYdo/cf4_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh5.ggpht.com/-3HZJhWG-r48/Tv3l85zcJTI/AAAAAAAACW8/tGymzXbEfqw/s1600-h/cf7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf7" border="0" alt="cf7" src="http://lh6.ggpht.com/-s4iA3HYrero/Tv3l_e7DRLI/AAAAAAAACXA/PGciLXmV4Nc/cf7_thumb.png?imgmax=800" width="240" height="144"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;クリックして拡大すると見えると思いますが、/Photos/Image配下はCDNへ。その他の要求(JavaScript/CSS/Ajax)は自サーバーに行ってますね。もちろんホントはJavaScriptやCSSも持っていくのがいいでしょう。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-gpF-rp3c0kY/Tv3mAMkdz-I/AAAAAAAACXM/2TQ8BBvt1dE/s1600-h/cf5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf5" border="0" alt="cf5" src="http://lh5.ggpht.com/-9a57sQ0s8QA/Tv3mBLMfJmI/AAAAAAAACXQ/G8_7ben-qIk/cf5_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh6.ggpht.com/-8vAMUiQQKTY/Tv3mBpECSsI/AAAAAAAACXY/ZD-JeNY2GVs/s1600-h/cf6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cf6" border="0" alt="cf6" src="http://lh5.ggpht.com/-eQ189_VOdvU/Tv3mCfByZJI/AAAAAAAACXk/Pex3kizp9h0/cf6_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;↑こちらは/mobile配下。今度は同じ/Photos/Image配下のものもCDNに行かず、自サーバー参照のままです。&lt;/p&gt;
&lt;p&gt;なんてお手軽。&lt;/p&gt;
&lt;p&gt;実際HTTPSを利用してるサイトでBLOBコンテンツのダウンロードが大量に発生している場合、この方法でサーバーへのコネクションを１／１０とかに抑えられることになって、ウハウハ。もちろんお金はかかりますけど。それでもサーバー増強やSSLアクセラレーションする機器の購入に比べれば、安いし経費で落とせます！HTTPSでのカスタムドメインが使えないのは気に入らない、って言うことがあるなら他の方法をとってくださいってことになります(ARRでSSLオフロードとかね、お金貰えればなんでもいいでしょう)。&lt;/p&gt;
&lt;p&gt;とりあえず、今回のような方法でCDNを利用する時に、こういうふうにページ(View部)作るといいかもと、思ったことを書きだしておきます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;素材を意味ごとに決まったフォルダに分けておく&lt;br&gt;→ パターンを増やすとその分の処理にCPU使っちゃう 
&lt;li&gt;スクリプトでスクリプトをロードしないほうがいいかも&lt;br&gt;→ スクリプト内のパスを書き換えるのが面倒 
&lt;li&gt;スクリプト内に極力パスを書かない&lt;br&gt;→ HTMLの属性を使う。DOMにデータを持たせとく。か、クライアントサイドのHTML Templateで。 
&lt;li&gt;同じくCSSの中でimportしないほうがいいかも&lt;br&gt;→ 相対パスなら許容可能 
&lt;li&gt;HTML内のsrc/hrefはルートからの絶対パスで書いておく&lt;br&gt;→ 相対パスだとパターンがぶれるし、思わぬ参照先に。どうしても相対ならCSSにする 
&lt;li&gt;HTML内でスタイル指定しない&lt;br&gt;→ パスの書き換え問題。同じ理由でタグ要素にstyle属性も困る。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;トリッキーなことしないで、メンテナンス性を考慮したページの作り方をしてれば、CDNへの振り向けはすんなり行きやすいという感じですね。&lt;/p&gt;
&lt;p&gt;目指せ Ultra-Fast ASP.NET！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-1191731882675718984?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/bKX4kxTQFhU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/bKX4kxTQFhU/urlrewritecloudfront.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/-w0T3Fi82kB8/Tv3l2HsON3I/AAAAAAAACWA/tKBt7FAFBh4/s72-c/cf2_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/12/urlrewritecloudfront.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-6665045202780428961</guid><pubDate>Tue, 27 Dec 2011 11:03:00 +0000</pubDate><atom:updated>2011-12-27T22:57:51.205+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><title>新しい.NET PaaS</title><description>&lt;p&gt;&lt;a href="http://www.ironfoundry.org/"&gt;Iron Foundry&lt;/a&gt;っていうのがあるらしいですね。&lt;a href="http://www.tier3.com/"&gt;Tier 3&lt;/a&gt;っていうIaaSの会社が、Cloud Foundry(vmware発のオープンソースPaaSフレームワーク)を利用して、.NET PaaSを自社クラウド上のサービスとして提供。&lt;/p&gt; &lt;p&gt;&lt;a href="http://blog.tier3.com/index.php/2011/12/tier-3-net-framework-cloud-foundry"&gt;Tier 3 Contributes .NET Framework Support for Cloud Foundry™ Platform as a Service to the Open Source Community « Tier 3 Company Blog&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;a href="http://www.infoq.com/jp/news/2011/12/dotnet-cloudfoundry"&gt;InfoQ: Tier 3がCloud Foundryで.NET Frameworkをサポート&lt;/a&gt; &lt;/p&gt; &lt;p&gt;この記事を見るまで、Cloud Foundryを全然しらなかったっす。面白いですね。IaaSのレイヤに依存しないPaaSフレームワーク。&lt;/p&gt; &lt;p&gt;なるほどー。面白そうついでにどんなものなのか、ちょびっとだけ触ってみました。触っただけなので中身はないです。具体的に知りたい(Cloud Foundryそのもの)場合は、ソースを見るといいかもね！&lt;/p&gt; &lt;p&gt;&lt;a href="https://github.com/IronFoundry"&gt;IronFoundry's Profile – GitHub&lt;/a&gt; &lt;/p&gt; &lt;p&gt;いろいろ試してたんですけどね、勝手が分からないっていうのもあるかもしれないけど、とにかく情報がNai！どーなんよ！&lt;/p&gt; &lt;p&gt;まずはIronFoundryのサイトからCloud Foundry ExplorerとVS2010用のVisual Studio Extensionをダウンロードしてインストール。&lt;/p&gt; &lt;p&gt;Sign upしてもSign inページもなくて「どーこー」。まぁね、サイトにサインインする場所なんか無いわけです。すべてツール経由。&lt;/p&gt; &lt;p&gt;手順は&lt;a href="http://help.ironfoundry.org/home"&gt;コミュニティサイト&lt;/a&gt;に書かれてるのでその通りにどーぞ。&lt;/p&gt; &lt;p&gt;&lt;a href="http://help.ironfoundry.org/entries/20777621-using-the-cloud-foundry-explorer-for-windows"&gt;Using the Cloud Foundry Explorer for Windows : Iron Foundry&lt;/a&gt; &lt;/p&gt; &lt;p&gt;ちなみに単体起動したCloud Foundry ExplorerとVS起動したものとで、プロファイルが共有されてないっぽい。ので、試すときはVSからの起動だけにしてしまいましょう。入力が2度手間。&lt;/p&gt; &lt;p&gt;んで、何より初期選択肢にIronFoundryのURLが登録されてない！(api.gofoundry.net)ので、そこから自分で登録。凄いね！さすが出たばっかり。その辺はご愛嬌。&lt;/p&gt; &lt;p&gt;試しに起動したサイトは↓こちら。&lt;/p&gt; &lt;p&gt;&lt;a href="http://takepara.gofoundry.net/"&gt;たけはらの国&lt;/a&gt;&lt;/p&gt; &lt;p&gt;分かりやすくServerVariables全部書き出しときました。&lt;/p&gt; &lt;p&gt;どういう設定になってるかというと↓こんなです。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-8I8U3PTscyE/Tvml6zkf2cI/AAAAAAAACUs/qEpyIdj_qiI/s1600-h/if%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="if" border="0" alt="if" src="http://lh4.ggpht.com/-Qc9zT_YLN40/Tvml7W_plRI/AAAAAAAACUw/LwiEUZnqp4o/if_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;クリックして拡大してね。&lt;/p&gt; &lt;p&gt;右下がインスタンス。メモリ512MBのインスタンス４つ起動しました。なんでかというと、アカウントに2048MBシバリがあるから。それ以上のメモリを必要とするインスタンスは起動できなかったです。LOCAL_ADDRの値がInstanceのHostアドレスと同じものが繰り返し表示されることが確認できるのできちんと４つのインスタンスに振り分けられてるのがわかります。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-04_JyYr4T48/Tvml8C6GAtI/AAAAAAAACU4/JLS4fXhLb_Q/s1600-h/if2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="if2" border="0" alt="if2" src="http://lh3.ggpht.com/-AFp56swW5X4/Tvml9FMRAxI/AAAAAAAACVE/wqUdK8FTb_8/if2_thumb.png?imgmax=800" width="240" height="129"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;メモリは↑この中から選択して起動できました。インスタンスの合計が2048MBを超えなければいいので、インスタンス毎にメモリ容量が違っても起動できます。なるほどね。これって、どういうことですかね。AppDomainのメモリ上限指定なんでしょうか。超えるとリサイクルされるアレ。それともWeb.configに指定してるのかな？と思って、ちゃんとWeb.configがどういうふうになってしまうのか(変更されるはず)を見るためのページも用意。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-JKCWxP8_AAA/Tvml-WIytEI/AAAAAAAACVM/BxBXC2O5p7A/s1600-h/if3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="if3" border="0" alt="if3" src="http://lh6.ggpht.com/-bYdeJ1HvFRI/Tvml_IeQErI/AAAAAAAACVU/insm0TYpq7w/if3_thumb.png?imgmax=800" width="240" height="137"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;どこまで隠したほうがいいのかよく分からないから、一応それっぽいところはモザイク。各自で確認してみてね！&lt;/p&gt; &lt;p&gt;DBがSQLServer認証だから、ConnectionStringにuidとpwdが埋まったものが自動追加されてて、AppSettingsにイロイロ追加されます。AppSettingsの値を見て挙動を制御するといいようですが。なるほど。でも項目の意味がわかりません！&lt;/p&gt; &lt;p&gt;えと、パッと見、Web.configにはメモリ制限らしきものは無いですね。VM自体に割り当てるメモリ量なのかなー。それにしては起動がめちゃめちゃ早いのが気になる。その辺Cloud Foundryに詳しい人なら知ってるのかな。まぁ、いいや。&lt;/p&gt; &lt;p&gt;VCAP_SERVICES/VMC_SERVICESそれぞれの値の中にplan:freeっていうのが見えます。無料お試しアカウント！&lt;/p&gt; &lt;p&gt;最初のスクリーンショットの真ん中したが各種サービスの登録で、選択できるのはMSSQLとMongoDB。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-o9jMOFFESVY/Tvml_9r7cmI/AAAAAAAACVc/_a0xQsFjjNU/s1600-h/if4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="if4" border="0" alt="if4" src="http://lh4.ggpht.com/-4azIvC4UFwo/TvmmAp9oJtI/AAAAAAAACVg/RnULWNkAl9c/if4_thumb.png?imgmax=800" width="240" height="113"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;でも、MongoDBはエラーになって作れませんでした。んじゃ、MSSQLは？と思ってどんどこ作って行ったら10個までは何も言われずに作成できちゃいましたよ。どこまで作れるんだろ。怖くなって10個でやめちゃった。実際には、ServicesのアイテムをドラッグしてApplication Servicesにドロップして初めて利用可能になるみたい。なので、そういうふうにしたら、Web.configに"Default"って言う名前で接続文字列が追加されました。ココはまだちょっと不具合があるみたい。なんかちゃんと生成した文字列が追加されてる気がしない。DBの容量がいくらに制限されてるのかの情報も見つけられない．．．。すげーぜ！&lt;/p&gt; &lt;p&gt;&lt;a href="https://github.com/IronFoundry/ironfoundry/blob/master/src/IronFoundry.Dea/Agent/FilesManager.cs"&gt;ソースを確認&lt;/a&gt;してみると、ちゃんとサービスのクレデンシャルからInitial Catalogを指定するようになってたり、すでに"Default"って言う名前のものがあったら、そこに追加する(SqlConnectionStringBuilderを使ってる)ようになってるんだけど、そういう動きをしてくれない。&lt;/p&gt; &lt;p&gt;しょうがないので、接続文字列は自分で追加しておきました。追加される接続文字列を参考にDefaultConnectionっていう名前でね。なんか、VSからのデプロイ時に確定させたらPaaSのメリットが～、とは思うけど、いつかきっとちゃんと動くようになるはずだから、今はこれでもいいかな。ちなみにVSの発行機能とは別の機能(Cloud Foundryの機能)でデプロイされるから、Web.configの構成変更が全然動いてくれない。これもまた残念。ソースがあるんだからカスタムしちゃえばいいかもしれないけどね。試したいだけだからスルー。&lt;/p&gt; &lt;p&gt;で、結局Initial Catalogが分からないので、SQL実行ページを用意して、SQLServerに問い合わせ(Universal ProviderがInitial Catalog必須。デフォルトデータベースに指定があってもダメなんす)。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-7LK4QphwogU/TvmmBNMTLcI/AAAAAAAACVs/MO3REVD9edg/s1600-h/if5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="if5" border="0" alt="if5" src="http://lh4.ggpht.com/-TAlo05soH0M/TvmmCF4qpbI/AAAAAAAACVw/Hquey3kKRUg/if5_thumb.png?imgmax=800" width="240" height="206"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;なるほどね。そんな文字列なのか。ん？これってVCAP_SERVICESのcredentialsのnameの値。なるほど。ソースは正しくかかれてるんだね。ちゃんと生成してくれればいいのに。&lt;/p&gt; &lt;p&gt;後は、MachinKeyをちゃんとWeb.configに書きたして、晴れてログインまで動くようになりました。Sessionは．．．。まぁ、いいや。&lt;/p&gt; &lt;h3&gt;Web.configの表示とSQL実行のコード&lt;/h3&gt;&lt;pre class="prettyprint"&gt;[Authorize(Users = "許可したいアカウント")]
public class SecretController : Controller
{
  public ActionResult Config()
  {
    var config = XDocument.Load(Server.MapPath("~/web.config"));

    return View(config);
  }

  public ActionResult Database()
  {
    var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
    ViewBag.ConnectionString = connectionString;

    return View();
  }

  [HttpPost]
  public ActionResult Database(string sql)
  {
    if (string.IsNullOrWhiteSpace(sql))
      return View();

    var response = new StringBuilder();
    var connectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
    ViewBag.ConnectionString = connectionString;

    using (var connection = new SqlConnection(connectionString))
    using (var command = new SqlCommand(sql, connection))
    {
      connection.Open();

      var reader = command.ExecuteReader();
      var table = new DataTable("result");
      table.Load(reader);

      var writer = new StringWriter(response);
      table.WriteXml(writer);
    }
    ViewBag.Database = response.ToString();
    return View();
  }
}
&lt;/pre&gt;
&lt;p&gt;雑．．．。どんまい。&lt;/p&gt;
&lt;h3&gt;ConfigのView&lt;/h3&gt;&lt;pre class="prettyprint"&gt;@model System.Xml.Linq.XDocument

@{
    ViewBag.Title = "Config";
}

&amp;lt;h2&amp;gt;Config&amp;lt;/h2&amp;gt;
&amp;lt;pre&amp;gt;
@Model.ToString()
&amp;lt;/pre&amp;gt;&lt;/pre&gt;
&lt;h3&gt;DatabaseのView&lt;/h3&gt;&lt;pre class="prettyprint"&gt;@{
    ViewBag.Title = "Database";
}

&amp;lt;h2&amp;gt;Database&amp;lt;/h2&amp;gt;
ConnectionString: @ViewBag.ConnectionString
&amp;lt;h3&amp;gt;SQL&amp;lt;/h3&amp;gt;
@using (Html.BeginForm())
{
    @Html.TextArea("sql",new{cols=80,rows=10})
    &amp;lt;button type="submit"&amp;gt;実行&amp;lt;/button&amp;gt;
}
&amp;lt;h3&amp;gt;Result&amp;lt;/h3&amp;gt;
&amp;lt;pre&amp;gt;
@ViewBag.Database
&amp;lt;/pre&amp;gt;&lt;/pre&gt;
&lt;p&gt;Iron Foundry自体ソースが公開されてるから、Tier 3じゃなくても自分で環境作って(SQLServer最新にしてMongoDBも最新にして、とか待たなくても自分で用意すれば解決するし)、実行環境にすることもできるので、コレはコレで面白い選択肢。知っといて損はなさそーですね。今後にも期待できそうだし！&lt;/p&gt;
&lt;h4&gt;追記&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://takepara.tumblr.com/post/14860074372/paas-cloud-foundry-publickey"&gt;追跡調査&lt;/a&gt;の結果、メモリ制限はリサイクルのプライベートメモリ制限でした！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-6665045202780428961?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/3AV9C2ffyV4" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/3AV9C2ffyV4/net-paas.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/-Qc9zT_YLN40/Tvml7W_plRI/AAAAAAAACUw/LwiEUZnqp4o/s72-c/if_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/12/net-paas.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-6513715582186631235</guid><pubDate>Fri, 16 Dec 2011 16:33:00 +0000</pubDate><atom:updated>2011-12-17T01:55:49.622+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Amazon AWS</category><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><title>MongoDBを本格利用するために</title><description>&lt;p&gt;MongoDB使ってますか？RavenDBのほうがいいですか？それともCassandraですか？いやいや、NoSQLなんて使わない派ですか？RDBよりも使い勝手のいいところがイロイロあると思うので、積極的につかてみてはどうでしょう。&lt;/p&gt; &lt;p&gt;先日MongoDBのSessionStateStoreに手を加えて、高速化してみたわけですが、そもそもMongoDBをどこに構築しておきましょう。自社内サーバー？DC内サーバー？いろいろ悩ましいところだと思いますが、ココは思い切ってAWS EC2に構築するのはどうでしょう。最近AzureでもOpennessでMongoDBが取り上げられてましたね。それもいいですけど、今回はEC2です。&lt;/p&gt; &lt;p&gt;&lt;a href="http://enterprisezine.jp/article/detail/3574"&gt;おまたせしました。楽しいMongoDBの時間デス（1/5）:企業のIT・経営・ビジネスをつなぐ情報サイト EnterpriseZine (EZ)&lt;/a&gt; &lt;/p&gt; &lt;p&gt;MongoDBも2になってからはジャーナルが標準だし、ReplicaSetとShardingもあるし、アクセス認証もかけれるしで、良い感じですよね！&lt;/p&gt; &lt;p&gt;がしかし！つい最近までは致命的な問題があったんです。認証使えるはずなのに、Sharding環境で認証をかけてるとC# Official Drivderが例外おきて接続できなくて、実質認証を使えないという問題。DC内ならいいけど、クラウドに持って行こうと思うと、これが非常に都合が悪い。パフォーマンスを考慮すると大変都合が悪かったです。&lt;/p&gt; &lt;p&gt;そもそもは、なんでMongoDBを運用環境に利用しようとしたか、ですけど、そんな話はどーでもいいですね。ちなみになんで運用環境に持って行こうと考えたかというと、SQLServerのミラーリングのコストが馬鹿にならなくなってきたから。あとログを1箇所に集約したかったっていう理由も大きい。イベントログめんどくね？フォワーディングするにしても、操作しにくいし。DBのマシン性能が低いっていう問題があったんだけど、そこに投資しないと解決出来ないってわけでも気がしたんですよね。違うアプローチで解決出来そうだとゴーストがささやいた。もちろん投資すれば一発解決。大人の解決方法です。でも、なんかNoSQLで解決できる気がしたんすよ。そういうことあるでしょ？&lt;/p&gt; &lt;p&gt;&lt;a href="http://takepara.blogspot.com/2011/10/tracelistner-into-mongodb.html"&gt;TraceListner into MongoDB&lt;/a&gt;&lt;br&gt;&lt;a href="http://takepara.blogspot.com/2011/11/asp.html"&gt;MongoDBにASP.NETのSessionを格納する&lt;/a&gt;&lt;br&gt;&lt;a href="http://takepara.blogspot.com/2011/11/apache-bench100001000-n-1000-c-8.html"&gt;MongoDBにASP.NETのSessionを格納する - 完結編&lt;/a&gt;&amp;nbsp;&amp;nbsp; &lt;/p&gt; &lt;p&gt;ASP.NETのセッションデータと、トレースログをSQLServer以外に保持したい。そのために必要なパーツは用意して、パフォーマンスとか可用性とか拡張性とか検証して行けそうだと感じたら、どこに構築しましょうか、ってことになりますよね。DC内に構築するのがネットワーク的にも近いし、効率いいのはそうなんですけど、んじゃサーバーどこから調達するのさー、ってなるし、よく分からないものに投資してくれろ、とは言いづらいしそもそも投資するならまずDBだろ、みたいな事になって行ったり来たり。むふー。とりあえずは、仮想マシン用意してその中にMongoDBの構築を行うっていうところでスモールスタートするわけですが、それも時限式。余裕ない。こうなったら、本格的にクラウドで構築するしかねー！MongoHQっていう選択肢も無いわけじゃないけど、いっその事、AWS EC2にがっつり構築するのがいいんじゃないかと、一人プロジェクトスタート。クラウド使ってるっていうとなんかカッコイイ気がするじゃない。&lt;/p&gt; &lt;p&gt;イロイロ試した結果、ReplicaSetとShardingでmongodインスタンスを6個(rsを3台ずつ2シャード)、mongosインスタンス4個、コンフィグインスタンス3個。MongoDBだけでまずは13個。それとDNSが2台。コンフィグ2台とDNS2台は相乗りにして全13台。DNSはAWS内部での名前解決に使う目的と、既存ドメインのサブドメインで外向きの名前解決用。既存のDNSの管理者権限なんて欲しくないし、弄りたくもないので、既存DNSにはゾーン委譲だけしてもらうようにして、AWS用のサブドメインを用意しました。その方が自分で好きなように制御出来るもんね！なぜmongosが4個なのかって？スモールスタートなら2個でいいじゃん！と思うところですが理由は別。&lt;/p&gt; &lt;p&gt;予算の関係もあって、すべてマイクロインスタンスで構築。なので、DC内からのアクセスでVPCは除外。VPNが簡単でよかったんだけど、そもそもDCにVPNルータを設置する余裕すらない。スペースと電源ね。大丈夫だとは思うけど、EC2がマイクロじゃないと予算が辛い。だってこの時点ではまだ自腹。そんなに稼いでませんから！&lt;/p&gt; &lt;p&gt;で、まぁ、今のMongoDBのバージョン(2.0.1)じゃShardingで認証が出来ないってところにたどり着いてガックリしてたわけです。&lt;/p&gt; &lt;p&gt;&lt;a href="https://jira.mongodb.org/browse/SERVER-3929"&gt;[#SERVER-3929] Arbitrary replica set authentication "need to login" errors. - MongoDB&lt;/a&gt; &lt;/p&gt; &lt;p&gt;しょうがないよね。できないものは出来ないと諦めて、IP制限で接続制限かけることにしました。アクセス経路はDCからAWSなので暗号化はしておきたい。でもVPCは厳しい。そうなるとIPSecかSSL tunnelか。ELBにSSL tunnel出来る機能があるので、それがいいなと思ってたんだけど、ELBはIP制限かけられない。インスタンスに届いた時のIPはELBのIPだし、SSL tunnelだとHTTPなわけでもないからX-Forworded-Forなんか無いし。そうなるとIPSecでマシン間接続の暗号化かってことになって、IPSecの設定の勉強から。認証できれば一気に解決なのにー、と思いつつできないものはしょうがない。&lt;/p&gt; &lt;p&gt;なので結局選択肢はなくIPSec。初めてIP セキュリティポリシー使いました。ウィザードの使い方をちゃんと理解すれば大丈夫だった。あとネゴシエーションのためにUDP500,4500開けるのを忘れずに。この話長いから省略。&lt;/p&gt; &lt;p&gt;ちなみにMongoDBにアクセスするすべてのマシンにIPSecを登録するのは大変なので、Delegateを利用してポートフォワード。コレはコレで懐かしい感じですね。NIFTYの時以来。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.delegate.org/delegate/"&gt;DeleGate Home Page (www.delegate.org)&lt;/a&gt; &lt;/p&gt; &lt;p&gt;やっと、経路がセキュアになって、IP制限をかけれるようにもなったので、EC2で構築したMongoDBへのパフォーマンステスト。ただ残念ながら、IPSecしないのに比べて随分遅い。4:3くらいの比率で遅い。暗号化のコストが発生するからmongosがヒーヒー言ってる．．．。&lt;/p&gt; &lt;p&gt;先ほどmongos4台用意したといったのには意味があって、外向き(IPSecで外部から接続する場合)と内向き(AWS内のMonogDB以外のインスタンスからの接続用、暗号化しなくていいもんね)でそれぞれ構築しないとパフォーマンスしんどいからです。なので、外向きはIPSec対応mongos、内向きは素のmongos。それぞれ2台ずつ用意して計4台。&lt;/p&gt; &lt;p&gt;IPSecを使ったら遅いのはCPUがパワー不足なのも原因だからインスタンス大きいの使うしかないのかなー、と思いつつもそれでもSQLServerに入れるよりは早いし、まぁ、いいかと。いつの日か先のMongoDBの不具合が改修された暁には再度設定しなおそうと思い、まずはこれで開始。&lt;/p&gt; &lt;p&gt;いろいろ大変だね。OpenSSL使って証明書を作ったり、Delegateでゲートウェイ用意したり、IPSecの接続環境作ったり、そもそもMongoDBの環境作ることも含め、やること山盛り。それらのHowTo書こうと思うと大変面倒なので全てまとめて省略！勉強になったよ。MongoDBでReplicaSet+Shardingでの認証のやり方(ReplicaSetで個別にPRIMARYにユーザー登録して、mongos起動後configサーバーにもユーザー登録、最後にauthとkeyFile指定してサービス化。ちなみにそこまではコンソール起動でちょこちょこ調整しながら作業しました)がわかりにくかった。これまでのバージョンでうまくいってない部分だったみたいだからやむなし。インフラ屋って大変ね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.openssl.org/"&gt;OpenSSL: The Open Source toolkit for SSL/TLS&lt;/a&gt; &lt;/p&gt; &lt;p&gt;今年ももう十分やりきっただろ。と、思った矢先にMongoDB2.0.2リリースのお知らせ。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/downloads"&gt;Downloads – MongoDB&lt;/a&gt; &lt;/p&gt; &lt;p&gt;数日前にRC2が出たときには今年はもうないな、と思ってたのに。ウズウズ。ワクワク。ちなみにRCで先の不具合が解消されてたのは確認済みだったので、リリースをひたすら待つだけだったんですけど、まさかこのタイミングで出てくるとはねー。&lt;/p&gt; &lt;p&gt;こうなるとELBを利用してSSL tunnel(ELBではSecureTCPっていう名前)を利用する環境を用意してしまおうじゃないかとなりましょう。ReplicaSet+Shardingで認証かけれれば、後は経路の問題だけだし、何よりELBで暗号化復号化してくれればmongosが通信に専念すればいい状態になるし、更にはELBがラウンドロビンでmongosを選んでくれるから、パフォーマンスは大幅向上。1.25倍(2倍近くさばける時もある)くらい早くなりました。ELBだとアクセスが増えると受け口を自動でスケールしてくれるので、負荷がもっと高くなると、5分くらいで自動でよしなにしてくれるらしい。5分だっけ？そこはいいか。そうするとIPSec用mongosは用なし。さらにEIPでグローバルも不要。DNSにELBのCNAMEを登録すればいいだけになって更にお買い得。&lt;/p&gt; &lt;p&gt;運用の目処もたち、Reserved Instanceで見積もったり(超間違えたけど)して、最終的にやっと会社が払ってくれるようになって一安心。DBもWebもある程度はAWSに持っていっちゃう予定だけど、最終形態はDCとクラウドのハイブリッドが良い感じに思えますが、一般的にはどうなんだろ。&lt;/p&gt; &lt;p&gt;今はまだテスト環境のセッションストアと、トレースログの取得だけにしか使ってないけど、徐々に全てのトラフィックをDC内のMongoDBからAWSのMongoDBに変更していく予定です。どーなるかなー。楽しみだなー。&lt;/p&gt; &lt;p&gt;せっかくなのでPPT用の素材を使って今回の構成図を書いてみたよ。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;MongoDB 2.0.1でIPSecの時&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-YVtziPegrrU/Tuty3WVKnEI/AAAAAAAACUM/XCz8OSNV2Bg/s1600-h/%2525C2%2525B9%2525C3%2525A9%2525C2%2525A4%2525C3%2525891%25255B10%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="スライド1" border="0" alt="スライド1" src="http://lh5.ggpht.com/-SzH2JfRlUeA/Tuty4E6Jz9I/AAAAAAAACUU/IlhT1cwLGIw/%2525C2%2525B9%2525C3%2525A9%2525C2%2525A4%2525C3%2525891_thumb%25255B6%25255D.png?imgmax=800" width="380" height="285"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;MongoDB 2.0.2でELBの時&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-6Gln06nXDlc/Tuty5PadQBI/AAAAAAAACUc/y2JeFZKHQUs/s1600-h/%2525C2%2525B9%2525C3%2525A9%2525C2%2525A4%2525C3%2525892%25255B12%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="スライド2" border="0" alt="スライド2" src="http://lh6.ggpht.com/-egR97hKHpNw/Tuty7d5VS3I/AAAAAAAACUk/GHtCiwvAySs/%2525C2%2525B9%2525C3%2525A9%2525C2%2525A4%2525C3%2525892_thumb%25255B8%25255D.png?imgmax=800" width="380" height="285"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;あは。センスないー。素材があってもコレじゃ．．．。なんとなくでも伝わるといいな。mongosからReplicaSetをShardingとして参照するように設定してるのをうまく表現できませんでした．．．。&lt;/p&gt; &lt;p&gt;ところで、CloudWatchって数値高めに出てない？実際ログインして直接パフォーマンス見てみると、CloudWatchで表示されるほど高くないんだけどなー。なんだろ。そんなもの？&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-6513715582186631235?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/EzEii5hBxTU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/EzEii5hBxTU/mongodb.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-SzH2JfRlUeA/Tuty4E6Jz9I/AAAAAAAACUU/IlhT1cwLGIw/s72-c/%2525C2%2525B9%2525C3%2525A9%2525C2%2525A4%2525C3%2525891_thumb%25255B6%25255D.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/12/mongodb.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-8182345746191662340</guid><pubDate>Sun, 20 Nov 2011 09:44:00 +0000</pubDate><atom:updated>2011-11-20T18:55:27.375+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">C#</category><title>MongoDBにASP.NETのSessionを格納する - 完結編</title><description>&lt;p&gt;前回のあらすじ&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;不思議な光る石のペンダントをつけた少女が空から降りてきた。天使が舞い降りたと思い走って近づく少年。その少女は伝説となった天空の城の王の末裔。少年もまた亡き父の言葉を信じ、天空の城の存在を信じていた。悪意を持った別の王の末裔が少女をさらい、城を悪用しようとすることから、少年は少女と城を守るため、海賊とともに空に旅立った。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;ドキドキするストーリーですね！&lt;/p&gt; &lt;p&gt;．．．。&lt;/p&gt; &lt;p&gt;続きです。最終的に接続エラーが出て、処理が継続できない状態に陥るっていうところでした。問題解決する前に、現時点でのパフォーマンスを確認してみます。&lt;/p&gt; &lt;p&gt;前回同様Apache Benchで実行しますが、10000回だとエラーになってしまって計測に失敗してしまうので、桁を下げて1000回(-n 1000 –c 8)としてましょう。&lt;/p&gt; &lt;p&gt;計測方法はabを3回実行してRequest per secondの平均を取ります。&lt;/p&gt; &lt;h3&gt;InProc&lt;/h3&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-qIQWcrxCS-Y/TsjLraZ2DVI/AAAAAAAACRY/L93Zwky80xA/s1600-h/ms1%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms1" border="0" alt="ms1" src="http://lh5.ggpht.com/-0hd6ZKs48Z4/TsjLrzKJpAI/AAAAAAAACRc/7JH_yChJYIM/ms1_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;1回目：538.39&lt;br&gt;2回目：588.10&lt;br&gt;3回目：660.41&lt;/p&gt; &lt;p&gt;&lt;strong&gt;平均：595.63&lt;/strong&gt;&lt;/p&gt; &lt;h3&gt;StateServer&lt;/h3&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-eABjaKlJ_8M/TsjLsWIyX9I/AAAAAAAACRk/sHvjO9V-AFY/s1600-h/ms11%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms11" border="0" alt="ms11" src="http://lh6.ggpht.com/-XZXAledvQ0U/TsjLs5lmP5I/AAAAAAAACRs/8vs3a0xjfCg/ms11_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;1回目：525.43&lt;br&gt;2回目：567.28&lt;br&gt;3回目：599.09&lt;/p&gt; &lt;p&gt;&lt;strong&gt;平均：563.93&lt;/strong&gt;&lt;/p&gt; &lt;h3&gt;SQLServer&lt;/h3&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-hTixVwJaC6M/TsjLtnWtv7I/AAAAAAAACR0/fl_BzWNThbs/s1600-h/ms2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms2" border="0" alt="ms2" src="http://lh3.ggpht.com/-NsKFA4biF_8/TsjLuYvNmkI/AAAAAAAACSA/Pk9AF2wfYxc/ms2_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;1回目：282.39&lt;br&gt;2回目：370.53&lt;br&gt;3回目：352.21&lt;/p&gt; &lt;p&gt;&lt;strong&gt;平均：335.04&lt;/strong&gt;&lt;/p&gt; &lt;h3&gt;MongoDB&lt;/h3&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-ULEj5MR3TtY/TsjLvAfEVgI/AAAAAAAACSE/sOhh-vtyapY/s1600-h/ms3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms3" border="0" alt="ms3" src="http://lh6.ggpht.com/-oBwqxv_uZco/TsjLvrlT72I/AAAAAAAACSQ/lRgLeLL8o0A/ms3_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;1回目：129.71&lt;br&gt;2回目：146.35&lt;br&gt;3回目：145.03&lt;/p&gt; &lt;p&gt;&lt;strong&gt;平均：140.36&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;うん。早くない。ちなみに無印UniversalProviderだと 30.15 とドンマイな感じ。&lt;/p&gt; &lt;p&gt;Universal Provider&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-M6eKCZqCZl0/TsjLwn2dkqI/AAAAAAAACSY/gHbDfMotRYc/s1600-h/ms4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms4" border="0" alt="ms4" src="http://lh6.ggpht.com/-7QN0G4ofOgY/TsjLxanc4nI/AAAAAAAACSg/6p7KaeUx1zA/ms4_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ちなみに、5回で平均を取りたかったけど、MongoDB版が5回目で接続エラーになって取れなかったので3回にしました。これは良くない！まずはちゃんと10000回普通に動かせるようにしないと。&lt;/p&gt; &lt;p&gt;と、いうわけで、MongoDB C# Official Driverのドキュメントに目を通してみると、内部でConnection Poolingを利用していると書かれています。が、挙動からはPoolがちゃんと利用されてないんじゃないかと、疑ってみました。 だって、接続エラーって．．．。使いまわしてるならそんなことにならないはず、という根拠のない診断。うさんくさいドクターハウス。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial"&gt;CSharp Driver Tutorial – MongoDB&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;a href="http://api.mongodb.org/csharp/current/"&gt;MongoDB C# Driver API Documentation - Table of Content&lt;/a&gt; &lt;/p&gt; &lt;p&gt;詳細はドキュメントから判断出来なかったので、ソースを追いかけてみました。オープンソースだといろいろ調べれていいですねー。&lt;/p&gt; &lt;p&gt;&lt;a href="https://github.com/mongodb/mongo-csharp-driver"&gt;mongodb/mongo-csharp-driver – GitHub&lt;/a&gt; &lt;/p&gt; &lt;p&gt;チラチラ見た結果からいうと、MongoServerクラスに保持しているMongoServerInstanceがConnectionPoolを管理していて、MongoServer.Disconnectを呼ぶとすべてのPoolをクローズして回る仕組みのようです。なんでだろ。理由はいいとして、そういう作りなんだということは、finallyで必ずDisconnectを呼び出すのはよろしくないかなー。そんな事しなくても、Poolに保持しているものは10秒ごとにMaxConnectionLifeTime/MaxConnectionIdleTimeを確認して破棄されるようになってるみたいなので、ほっといてもいいんじゃね？みたいな。&lt;/p&gt; &lt;p&gt;と、いうわけで、ソース中のすべてのDisconnectを削除します。&lt;/p&gt; &lt;p&gt;その状態で、abで確認します。&lt;/p&gt; &lt;p&gt;3回実行した結果は525.43 / 599.09 / 572.34。&lt;strong&gt;平均：565.62&lt;/strong&gt;。ワォ！一気に上がりました。せっかくなので10000回出来るかどうかもチェック。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-K1ssg91NeiI/TsjLyPWT2zI/AAAAAAAACSk/yIbH5A0rvac/s1600-h/ms5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms5" border="0" alt="ms5" src="http://lh4.ggpht.com/-C-TrW9w2iio/TsjLyjjtSMI/AAAAAAAACSs/qMThFep15Hc/ms5_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;行けるようになりましたー。ちゃんと接続を使い回してくれてるようです。パフォーマンスも接続を使いまわすことで一気に改善しました。んじゃ、なんで、そういう作りにしてなかったのか？っていうのが気になります。&lt;/p&gt; &lt;p&gt;理由はありました。どういうことかというと、Poolが保持しているMongoConnectionは生成された時点でReplicaSetのPrimaryを判定して保持しています。つまり、Failoverした直後、Poolが破棄されない限り接続エラーがおきて、正しくデータの書き込みができなくなるということでしょう。&lt;/p&gt; &lt;p&gt;abを実行し、その最中にstepDownでFailoverしてみます。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-v_ej_MJtYxc/TsjLzZaEG_I/AAAAAAAACS0/qsa7Ok4fAvo/s1600-h/ms6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms6" border="0" alt="ms6" src="http://lh5.ggpht.com/-2EtoN9YBpOs/TsjL0FLkNsI/AAAAAAAACS8/Pfxa0M2g5No/ms6_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;a href="http://lh3.ggpht.com/-0SJIddsTk5M/TsjL08SjSPI/AAAAAAAACTE/utRJZkdVGeM/s1600-h/ms7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms7" border="0" alt="ms7" src="http://lh5.ggpht.com/-OichX5gefs8/TsjL1tgmnrI/AAAAAAAACTQ/olkt0gITF3U/ms7_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Safemode detected an error 'not master'. (Response was { "err" : "not master", "code" : 10056, "n" : 0, "lastOp" : NumberLong("5676991341247070789"), "connectionId" : 13356, "ok" : 1.0 }).&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;masterじゃないダニ！と言われ、書き込みできないエラーが発生しました。SafeModeじゃなくても同じようにエラーになることは確認済み。&lt;/p&gt; &lt;p&gt;と、得意げにドヤ顔したところで、それはつまりDisconnectしなくなったからでしょ？と、おっしゃるとおりな理由でのエラーなんですけどね。つまり、Failoverした後もちゃんと接続が継続できるようにするために毎回Disconnectしてたわけですね。&lt;/p&gt; &lt;p&gt;あちゃー、これはどうしたものか。なんて、あたかも今、解決策を思いつくような振りになってますけど、事前に調査してわかってることを書いてるだけなんで．．．。てへ！&lt;/p&gt; &lt;p&gt;つまり今回のエラーの原因はPoolから取り出したMongoConnectionがPrimary(Master)を指してないから例外が起きるわけです。そして、MongoServer.Disconnectを呼ぶことでPoolのすべてのコネクションが破棄されるというのはソース見て確認済み。つまりMongoDBに対する処理で例外が発生したらMongoServer.Disconnectを呼び出し、処理をリトライすることで、Failover後に決定したNodeに接続するようにしてあげればよい、という方法が取れましょう。MaxConnectionLifeTimeを短くしてみては？っていうのもあるでしょうが短すぎると接続エラーになりそうだし。&lt;/p&gt; &lt;p&gt;で、用意したのが以下のようなリトライ実行するヘルパー。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public class MongoDbInvokeSetting
{
  public string ServerName { get; set; }
  public string DatabaseName { get; set; }
  public int RetryCount { get; set; }
  public int RetrySeconds { get; set; }
}

public class MongoDbHelper
{
  /// &amp;lt;summary&amp;gt;
  /// リトライを繰り返すInvoker
  /// &amp;lt;/summary&amp;gt;
  /// &amp;lt;param name="setting"&amp;gt;&amp;lt;/param&amp;gt;
  /// &amp;lt;param name="functor"&amp;gt;&amp;lt;/param&amp;gt;
  public static void RetryInvoker(MongoDbInvokeSetting setting, Action&amp;lt;MongoServer&amp;gt; functor)
  {
    var retry = setting.RetryCount;
    Exception exception = null;
    while (retry &amp;gt; 0)
    {
      var server = MongoServer.Create(setting.ServerName);
      try
      {
        functor(server);
        break;
      }
      catch (MongoException ex)
      {
        exception = ex;
        // ↓こいつでPool内の全コネクションクリアが走るはず。
        server.Disconnect();
      }
      retry--;

      // とりあえずn秒待ってみる。
      Thread.Sleep(setting.RetrySeconds * 1000);
    }

    // ここでイベントログに書き出すか、Exceptionを出力する必要がある。
    // 後続の処理に進んだら困るならException。
    // ログなら進んでもいいかもねー。
  }
}
&lt;/pre&gt;
&lt;p&gt;ダサくない！ダサくないよ！これでいいんだもん！&lt;/p&gt;
&lt;p&gt;このコードを書いてる途中、さすがに心配になったんですけど(これでいいのかっていうね)、そんなおり以下のページを見つけました。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://api.mongodb.org/ruby/current/file.REPLICA_SETS.html"&gt;File: REPLICA_SETS — MongoRuby-1.4.1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;似たようなリトライさせてますね。だもんで、いいってことで。 &lt;/p&gt;
&lt;p&gt;これを使って実行する書き方は↓こう。&lt;/p&gt;&lt;pre class="prettyprint"&gt;MongoDbHelper.RetryInvoker(GetSetting(), server =&amp;gt;
{
  // serverを使ってCollection検索したり、
  // データ追加したり...
});
&lt;/pre&gt;
&lt;p&gt;GetSettingは設定を入れたものを返しましょう。サーバー名とかリトライ回数とか。&lt;/p&gt;
&lt;p&gt;これつかって丸っとしたもので、実行しつつFailoverさせてみます(abでエラーが出ないことを確認しました)。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-aYbn_wAu8Eo/TsjL2d0NPQI/AAAAAAAACTY/s7hVOmVqw4Q/s1600-h/ms8%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms8" border="0" alt="ms8" src="http://lh5.ggpht.com/-uaN3wvWROQU/TsjL3KkIdHI/AAAAAAAACTc/32v0il3mn9I/ms8_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;大成功！エラーも起きずFailover成功しました。データも欠落なくいけてます。&lt;/p&gt;
&lt;p&gt;で、この状態でのパフォーマスンスを測定してみます。&lt;/p&gt;
&lt;p&gt;3回実行したのが485.62 / 496.92 / 552.61 &lt;strong&gt;平均：511.72&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;遅くなったじゃん！っていう感じしますけど、あったまって無かったみたい。実質Disconnect削除のパフォーマンスと同じ数値でした。&lt;/p&gt;
&lt;p&gt;でも～、これだと～、なんていうかパンチが弱い。十分いいとは思うんだけど～。どこか早くできそうなところないかなー、とMongoSessionStateStoreのソースを見ているとありましたねー。&lt;/p&gt;&lt;pre class="prettyprint"&gt;private string Serialize(SessionStateItemCollection items)
{
  using (MemoryStream ms = new MemoryStream())
  using (BinaryWriter writer = new BinaryWriter(ms))
  {
    ...
    return Convert.ToBase64String(ms.ToArray());
  }
}

private SessionStateStoreData Deserialize(HttpContext context,
string serializedItems, int timeout)
{
  using (MemoryStream ms =
    new MemoryStream(Convert.FromBase64String(serializedItems)))
  {
    ...
  }
}

&lt;/pre&gt;
&lt;p&gt;ほら、ここ。Base64に変換してstringにしてるじゃないですか。コレいらなくない？BsonBinaryArray使えばbyte[]をそのまま入れることが出来るんじゃないのー？と、気になったので試した結果が↓こちら。&lt;/p&gt;
&lt;p&gt;3回実行したのが 542.95 / 538.68 / 534.19、 &lt;strong&gt;平均：538.61&lt;/strong&gt;。微妙．．．。まぁ、いいか。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-yD_CKeEqimA/TsjL3uIWDQI/AAAAAAAACTk/JNScgNWZTtw/s1600-h/ms9%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms9" border="0" alt="ms9" src="http://lh6.ggpht.com/-rRFONRlM11w/TsjL4azMWfI/AAAAAAAACTs/J0QwcBlj7Go/ms9_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ちなみにこのままだと、Expireしたセッション削除が残り続けるので、SetAndReleaseItemExclusiveの中で2000回に一回くらいクリーンアップするように仕込んでみました。その状態で10000回実行。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-5hcwSs_GluQ/TsjL5KMgOPI/AAAAAAAACT0/B2-xRG2Ll1E/s1600-h/ms10%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms10" border="0" alt="ms10" src="http://lh5.ggpht.com/-0Yt4pxmI4GE/TsjL54h511I/AAAAAAAACT8/imp9hzFoo1Q/ms10_thumb.png?imgmax=800" width="240" height="175"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;10000回中5回クリーンアップしてるけど、まぁまぁ。外部タスクにしてしまえば影響はでないものなので、ここはいいでしょう。&lt;/p&gt;
&lt;p&gt;ち・な・み・に、SQLServerだと平均335.04ですからね。いいじゃないですか。ねぇ。Shardingしてみても同じマシンだとあまり変化でなかったです。と、いうのもCPUを最も使ってるのがIISExpressのプロセスだったので、アプリケーションが遅いってことですから。あとは、マシン分けて計測しかないですが、少なくとも同一環境内ではMongoDBでのSessionStateStoreがStateServerに負けないくらいの速度を実現しました(何度か試したらStateServerはもっと早かったけどー、そこは情報操作！)。&lt;/p&gt;
&lt;p&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/SampleProjects/SessionTest.zip?cid=df0c51db6ad31f29&amp;amp;sc=documents" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-8182345746191662340?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/JD6bx7R9Yb0" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/JD6bx7R9Yb0/apache-bench100001000-n-1000-c-8.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-0hd6ZKs48Z4/TsjLrzKJpAI/AAAAAAAACRc/7JH_yChJYIM/s72-c/ms1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/11/apache-bench100001000-n-1000-c-8.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4209976830751713033</guid><pubDate>Sat, 19 Nov 2011 14:24:00 +0000</pubDate><atom:updated>2011-11-19T23:29:32.287+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">C#</category><title>MongoDBにASP.NETのSessionを格納する</title><description>&lt;p&gt;ASP.NETのSessionといえば&lt;/p&gt; &lt;ul&gt; &lt;li&gt;InProc(アプリけーションプロセス内のインメモリステート管理)&lt;br&gt;シリアライズコストも発生せず、同一プロセス内で言わばstatic Dictionary&amp;lt;string,object&amp;gt;の実装で最速。ただし、アプリけーションのリサイクルと同時にセッションが破棄されるのと複数プロセス間で共有できないです。  &lt;li&gt;StateServer(専用プロセスによるインメモリステート管理)&lt;br&gt;InProcの弱点である、アプリけーションのリサイクルによる破棄とプロセス間共有を実現出来るようにしたもの。プロセスをまたぐのでシリアライズコスト発生するけど、インメモリなのでInProcにつぐ速度を実現。ただし、シングルプロセスでの実装になるので、耐障害性という意味では弱い。ステートをホストしているマシンの障害や再起動などでセッションが破棄されてしまいます。  &lt;li&gt;SQLServer(ストーレジに保持するステート管理)&lt;br&gt;StateServerの弱点であり、耐障害性をクリアしたストレージ保存型でのセッション管理。シリアライズコスト＋DBサーバーのパフォーマンス次第で遅くもなるし早くもなる。  &lt;li&gt;Custom(SessionStateStoreProviderBaseを派生したカスタム実装)&lt;br&gt;キャッシュクラスタ(Velocity)を利用したり、EntityFrameworkを利用した実装(UniversalProvider)を使ったりSessionStateStoreに対してイロイロです。DBをSQLServer以外にする場合にも利用されてます。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;選択肢多くていいですね。標準のものもそれぞれ優秀で素晴らしいです。SessionIDの生成や、クライアントとのやり取りは別の仕組みで実装されてるので、SessionStateStoreProviderのカスタムとは分離されてて安心です。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.adathedev.co.uk/2011/05/mongodb-aspnet-session-state-store.html"&gt;MongoDB ASP.NET Session State Store Provider | AdaTheDev&lt;/a&gt; &lt;/p&gt; &lt;p&gt;最近、すっかりMongoDBに心奪われてる身としては、SessionをMongoDBに入れてしまいたいという衝動にかられてます。あれやこれやの問題もあり、SQLServerだけにたよるソリューションだとよろしくないなというのもありまして。&lt;/p&gt; &lt;p&gt;MongoDB、大変優秀ですね。&lt;a href="http://www.mongodb.org/display/DOCSJP/Replica+Sets"&gt;ReplicaSet&lt;/a&gt;(レプリケーション)による可用性の確保(自動フェールオーバー有り)と、&lt;a href="http://www.mongodb.org/display/DOCSJP/Sharding"&gt;Sharding&lt;/a&gt;(データのパーティショニング)によるパフォーマンスの確保、素晴らしいです。&lt;/p&gt; &lt;p&gt;いろいろ試して部分的に利用を始めてる段階で、まだまだ自信をもって使いまくるって言う状況ではないですが、ちょっとずつです。ちょっとずつ使っていってSQLServer＋MongoDBで行けるところまで行ってみようと企んでます。&lt;/p&gt; &lt;p&gt;まずはTraceListnerのMongoDB化。前回のエントリでそれっぽい感じのものを提示しましたが、これでサーバー群のトレース情報を低コストなストレージに保持出来るようになりますね。トレース情報くらいなら万が一情報欠落しても致命的になることも無いでしょう。しかし！セッションはそういうわけにはいきませんね。システムとして提供している表の機能に影響がでちゃいますからね。&lt;/p&gt; &lt;p&gt;そうなると、ReplicaSetの機能を利用することは必須となりましょう。そうするとFailoverの時の挙動を確認したりする必要も出てきますよね。そういうテストを繰り返してこそですよね。楽しい時間ですね。&lt;/p&gt; &lt;p&gt;先ずはReplicaSetでMongoDBを２つ起動。ArbitarっていうSQLServerでいうところのウィットネスサーバーを１台追加してないと投票結果が偶数とかになって次のPrimary決定時に困っちゃうことがあるので、３ノード起動します(&lt;a href="http://www.mongodb.org/display/DOCSJP/Replica+Set+Tutorial"&gt;詳しくはオフィシャルサイト&lt;/a&gt;でどーぞ)。&lt;/p&gt; &lt;p&gt;rem arbitar&lt;br&gt;start "arbitar" c:\mongo\bin\mongod.exe --port 27020 --replSet mongo --dbpath c:\mongo\dba&lt;/p&gt; &lt;p&gt;rem replicaset&lt;br&gt;start "mongo1" c:\mongo\bin\mongod.exe --rest --noauth --port 27031 --replSet mongo --oplogSize 20 --dbpath c:\mongo\dbr1&lt;br&gt;start "mongo2" c:\mongo\bin\mongod.exe --rest --noauth --port 27032 --replSet mongo --oplogSize 20 --dbpath c:\mongo\dbr2&lt;br&gt;&lt;/p&gt; &lt;p&gt;これで起動してるので(c:\mongoに一式ある前提です)、まずは初期化。&lt;/p&gt; &lt;p&gt;27031のノードにMongoDB Shellで接続。&lt;/p&gt; &lt;p&gt;mongo localhost:27031&lt;/p&gt; &lt;p&gt;初期化コマンド順番に実行すればOKです。&lt;/p&gt; &lt;p&gt;rs.initiate()&lt;br&gt;rs.add("hostname:27031")&lt;br&gt;rs.add("hostname:27032")&lt;br&gt;rs.addArb("hostname:27020")&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-9cZVgckurJk/Tse7yMYCAtI/AAAAAAAACOo/n4bjtaMUcXU/s1600-h/ms%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms" border="0" alt="ms" src="http://lh3.ggpht.com/-VkDL66wI6EE/Tse7y4doTMI/AAAAAAAACOs/5JyrrAn08Gk/ms_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;まずはこの状態で公開されてるソースを利用してSessionを使ってMongoDBにデータを入れてみましょう。&lt;/p&gt; &lt;p&gt;MVC4DPでアプリケーションを作り、Home/IndexアクションでSessionに値をいれて、Home/Indexビューで表示するだけのものです。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public ActionResult Index()
{
  ViewBag.Message = "Modify this template to kick-start your ASP.NET MVC application.";
  Session["message"] = ViewBag.Message;

  return View();
}
&lt;/pre&gt;&lt;pre class="prettyprint"&gt;&amp;lt;h2&amp;gt;from session:@Session["message"]&amp;lt;/h2&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/AdaTheDev/MongoDB-ASP.NET-Session-State-Store/blob/master/MongoSessionStateStore/MongoSessionStateStore.cs"&gt;MongoSessionStateStore/MongoSessionStateStore.cs at master from AdaTheDev/MongoDB-ASP.NET-Session-State-Store - GitHub&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;↑ここからソースを取得して、プロジェクトに追加。後はNuGetでMongoDB Official Driverを入れておきましょう。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-Q-HeEJvNt_M/Tse7zcSRUNI/AAAAAAAACO0/Ba2IK9zb-JY/s1600-h/ms2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms2" border="0" alt="ms2" src="http://lh5.ggpht.com/-xY0iwCPvcrk/Tse70Im5YqI/AAAAAAAACPA/-4cj849sLCQ/ms2_thumb.png?imgmax=800" width="240" height="182"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;コメントに従い、接続文字列を指定するとレプリカセットにならないので、今回起動したMongoDBを指すように接続文字列を変更。&lt;/p&gt;
&lt;p&gt;connectionString="mongodb://localhost:27031,localhost:27032/"&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-1OMct8gn_Is/Tse71AnIY0I/AAAAAAAACPE/6Ql6sVayZXk/s1600-h/ms3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms3" border="0" alt="ms3" src="http://lh5.ggpht.com/-_U2lmpIWpRY/Tse71uODHeI/AAAAAAAACPM/n9StS8CRSro/ms3_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ちゃんと出来てますね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.mongovue.com/"&gt;MongoVUE&lt;/a&gt;で27031(primary)データを確認。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-C73t1FJfgbk/Tse72fc_shI/AAAAAAAACPU/k17iupnY69I/s1600-h/ms4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms4" border="0" alt="ms4" src="http://lh6.ggpht.com/-g0_2uarWG1Y/Tse73DtKAyI/AAAAAAAACPc/WLSEyjNSOuM/ms4_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;入ってるねー。&lt;/p&gt;
&lt;p&gt;続いて27032(Secondary)のデータを確認。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-I_sMDTMCC3M/Tse73qOm-TI/AAAAAAAACPk/EsJxbm-noAk/s1600-h/ms5%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms5" border="0" alt="ms5" src="http://lh6.ggpht.com/-GTCCqq7Ipxc/Tse75RB58II/AAAAAAAACPw/_49BXXM1OCs/ms5_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;もちろん入ってますね。&lt;/p&gt;
&lt;p&gt;ここで、ReplicaSetを入れ替えましょう！&lt;/p&gt;
&lt;p&gt;MongoDB ShellでPrimaryにつないで rs.stepDown() で強制フェールオーバーを実行。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-o_fpvEYSuPY/Tse75_jsS8I/AAAAAAAACP0/Xfl_f7Aehn8/s1600-h/ms6%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms6" border="0" alt="ms6" src="http://lh6.ggpht.com/-qlM9VFCXEwk/Tse76ToJQNI/AAAAAAAACP8/6A_pRBxgWWI/ms6_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ちゃんと切り替わりました。&lt;/p&gt;
&lt;p&gt;この状態で再度先ほどの起動したブラウザでF5でリロード。ちゃんと動くなら、これでFailoverしたほうを参照して、表示されるはず！&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-kCjLcl3TRKI/Tse77e89asI/AAAAAAAACQI/brhLLDsPgjw/s1600-h/ms7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms7" border="0" alt="ms7" src="http://lh4.ggpht.com/-GITybqbC0UI/Tse78XvvEoI/AAAAAAAACQM/MXdvfFiKjhs/ms7_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-Hv-3HkoPjJs/Tse788zaxGI/AAAAAAAACQU/hEQ-0hpeTq8/s1600-h/ms11%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms11" border="0" alt="ms11" src="http://lh6.ggpht.com/-7S6vF1ta720/Tse79vaovxI/AAAAAAAACQc/5ZPRpENYpso/ms11_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;タターン！うまくいきました。内部ではSafeModeっていう書き込み確認モードで動作するようになってるので、手堅いです。&lt;/p&gt;
&lt;p&gt;27032(SecondaryからPrimaryに変更したノード)でSessionsコレクション(テーブルですね)を確認して見つつブラウザのF5リロードを繰り返すと、ちゃんとLockIdがカウントアップしていくので、読み込みも書き込みもFailover後にちゃんとできてるのが確認できます。&lt;/p&gt;
&lt;p&gt;だがしかし！ここで大問題が！！&lt;/p&gt;
&lt;p&gt;パフォーマンスを測定しようとApache Benchでリクエストを投げまくってみると、最後まで完了せずにエラーで途中終了してしまいます。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-2FtzFWHByjI/Tse7-Ag7vlI/AAAAAAAACQg/PEH_nPheTZI/s1600-h/ms9%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms9" border="0" alt="ms9" src="http://lh3.ggpht.com/-QpRepin_OWA/Tse7-rit9TI/AAAAAAAACQo/zfRIjuOApM8/ms9_thumb.png?imgmax=800" width="240" height="118"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-RoiwlkNvRmg/Tse7_haLqFI/AAAAAAAACQ0/praKBVOa8R4/s1600-h/ms8%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms8" border="0" alt="ms8" src="http://lh4.ggpht.com/-7XoDvYANHp4/Tse8ARk3bhI/AAAAAAAACQ4/Pua1ilo4pus/ms8_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-8w599M0KqJo/Tse8AxmKT2I/AAAAAAAACRE/Bgzxcogjt3Q/s1600-h/ms10%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ms10" border="0" alt="ms10" src="http://lh4.ggpht.com/-Ed8ia6zZiC4/Tse8CDQ0exI/AAAAAAAACRI/2oR_U3KNX6M/ms10_thumb.png?imgmax=800" width="240" height="130"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unable to connect to the primary member of the replica set: システムのバッファー領域が不足しているか、またはキューがいっぱいなため、ソケット操作を実行できませんでした。 &lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;なかなかの男気あふれる強気のエラーメッセージ。&lt;br&gt;どーしたんだMongoSessionStateStore！&lt;br&gt;これが精一杯なのかOfficial Driver！&lt;/p&gt;
&lt;p&gt;長くなったのでつづく．．．。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4209976830751713033?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/TA0Jk533D0o" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/TA0Jk533D0o/asp.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-VkDL66wI6EE/Tse7y4doTMI/AAAAAAAACOs/5JyrrAn08Gk/s72-c/ms_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/11/asp.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4644639565505434318</guid><pubDate>Sat, 29 Oct 2011 01:15:00 +0000</pubDate><atom:updated>2011-10-29T10:15:42.327+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><title>URLRewriteのoutboundRulesでセッションIDを含んだHTML内のURLを普通のURLに戻す、リベンジ</title><description>&lt;p&gt;タイトル長っ！&lt;/p&gt; &lt;p&gt;&lt;a href="http://takepara.blogspot.com/2011/08/controllerhtml.html"&gt;無聊を託つ: Controllerを名前から生成するしHTMLを書き換えたりもしてみる&lt;/a&gt; &lt;/p&gt; &lt;p&gt;ちょっと前にエントリしました。が、間違えてました。思いっきり。これを信じてくれた人すいません。リベンジです！今度はこないだのよりだいぶマシ。&lt;/p&gt; &lt;p&gt;目的は、OutputCacheを利用する際にCookieless URL(セッションIDとかを含んだURL)をHTMLに保持してると、他の人とセッション共有しちゃうから、それを防ぐ！です。セキュリティ的に守らなきゃ、という理由だけでページ全体をno-cacheにするのはあまりにも富豪。&lt;/p&gt; &lt;p&gt;さらに、gzipで動的コンテンツを圧縮することで、CPUは多めに使うことがあるけど、OutputCacheで相殺。もちろんレスポンス性能が劇的に向上するので、ユーザーにとってはいいことづくし。&lt;/p&gt;&lt;pre class="prettyprint"&gt;&amp;lt;rewrite&amp;gt;
  &amp;lt;outboundRules&amp;gt;
    &amp;lt;rule name="Sessionless" preCondition="html" enabled="true"&amp;gt;
      &amp;lt;match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script, CustomTags" 
				customTags="All" 
				pattern="(.*)/\([SFA]\([^/]+\)\)/(.*)"/&amp;gt;
      &amp;lt;action type="Rewrite" value="{R:1}/{R:2}"/&amp;gt;
      &amp;lt;conditions&amp;gt;
        &amp;lt;add input="{REQUEST_URI}" matchType="Pattern" pattern="\)/mobile" ignoreCase="true" negate="true" /&amp;gt;
      &amp;lt;/conditions&amp;gt;
    &amp;lt;/rule&amp;gt;
    &amp;lt;customTags&amp;gt;
      &amp;lt;tags name="All" /&amp;gt;
    &amp;lt;/customTags&amp;gt;
    &amp;lt;preConditions&amp;gt;
      &amp;lt;preCondition name="html"&amp;gt;
        &amp;lt;add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html" /&amp;gt;
        &amp;lt;add input="{REQUEST_URI}" pattern="/mobile" negate="true" /&amp;gt;
      &amp;lt;/preCondition&amp;gt;
    &amp;lt;/preConditions&amp;gt;
  &amp;lt;/outboundRules&amp;gt;
&amp;lt;/rewrite&amp;gt;
&lt;/pre&gt;
&lt;p&gt;これが、たぶん正解のRewrite用のconfig。ちゃんと確認してみます。&lt;/p&gt;
&lt;p&gt;せっかくなので、MVC4DPを利用してみましょう(意味なくはない)。プロジェクト作って実行すると表示される画面は↓こうですね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-HXSgC3GHhw0/TqtTce4aumI/AAAAAAAACHw/8xxnu1iX7_I/s1600-h/rewrite12.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite1" border="0" alt="rewrite1" src="http://lh3.ggpht.com/-vUy34thkdII/TqtTdNsXvUI/AAAAAAAACH0/OduH47YZbz4/rewrite1_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;この時のURLは http://localhost:61972/ です。&lt;/p&gt;
&lt;p&gt;このままではやりにくいので、sessionStateでcookieless="UseUri"にします。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-GCOpwb7QsYo/TqtTd8Dqe5I/AAAAAAAACIA/uHscGTBO7oQ/s1600-h/rewrite22.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite2" border="0" alt="rewrite2" src="http://lh4.ggpht.com/-tVAy_zVFPYE/TqtTe4_RetI/AAAAAAAACIE/1w0XxUIiRZk/rewrite2_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;見た目が変わるわけじゃないです。URLを見てくださいね。今度は http://localhost:61972/(S(l2ulbdgxdrbdoyrozczknp1p))/ となっていますね。この状態でソースを確認します。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-HtnkEJPnVWQ/TqtTff1kQYI/AAAAAAAACIQ/cjC1-HI4Nig/s1600-h/rewrite32.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite3" border="0" alt="rewrite3" src="http://lh5.ggpht.com/-Te_AfDos58g/TqtTghVjwsI/AAAAAAAACIU/vs0L97pCZbY/rewrite3_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;バッチリセッションIDがURLに含まれてますね。先ほどのconfigをsystem.webServer内に追記するとどうなるか。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-azN-x-mDtEs/TqtThRpZEgI/AAAAAAAACIg/zTL3ZHkzAH4/s1600-h/rewrite42.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite4" border="0" alt="rewrite4" src="http://lh3.ggpht.com/-382ZPglPrGY/TqtTiZWyTeI/AAAAAAAACIk/sTEyEs7UKdk/rewrite4_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;セッションIDが消えました！概ね．．．。部分的に残ってる部分があるんですけど、それは多分仕様。と、いうのもAタグのhref属性の前にdata-dialog-title属性が入ってますよね。これがあるとURLRewriteが対象だと判断してくれないみたいです。試しに、_LogOnPartial.cshtmlを変更してみます。&lt;/p&gt;&lt;pre class="prettyprint"&gt;@if (Request.IsAuthenticated) {
    &amp;lt;p&amp;gt;
        Hello, @Html.ActionLink(User.Identity.Name, "ChangePassword", "Account", null, new { @class = "username" })!
        @Html.ActionLink("Log Off", "LogOff", "Account")
    &amp;lt;/p&amp;gt;
} else {
    &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink", &lt;font color="#ff0000"&gt;data_dialog_title = "Registration"&lt;/font&gt; })&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;@Html.ActionLink("Log on", "LogOn", "Account", routeValues: null, htmlAttributes: new { id = "logonLink", &lt;font color="#ff0000"&gt;data_dialog_title = "Identification"&lt;/font&gt; })&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
}&lt;/pre&gt;
&lt;p&gt;↑こっちがオリジナル。で、↓こっちが修正版。違いはhtmlAttributesのdata_dialog_titleの有無。&lt;/p&gt;&lt;pre class="prettyprint"&gt;@if (Request.IsAuthenticated) {
    &amp;lt;p&amp;gt;
        Hello, @Html.ActionLink(User.Identity.Name, "ChangePassword", "Account", null, new { @class = "username" })!
        @Html.ActionLink("Log Off", "LogOff", "Account")
    &amp;lt;/p&amp;gt;
} else {
    &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;@Html.ActionLink("Log on", "LogOn", "Account", routeValues: null, htmlAttributes: new { id = "logonLink" })&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
}&lt;/pre&gt;
&lt;p&gt;そうするとレンダリングされるHTMLは↓こうなります。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/--r4TSDo6_pU/TqtTi-gIr-I/AAAAAAAACIs/3Kv3jO_evIE/s1600-h/rewrite52.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite5" border="0" alt="rewrite5" src="http://lh6.ggpht.com/-Ib2t25lTamA/TqtTjs8oJzI/AAAAAAAACI0/v2i6tG7Kv8o/rewrite5_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;RegisterとLog onのURLからもSessionIDが消えて正しくなりました。htmlAttributesで追加した属性はTagBuilderで展開されるときにアルファベット順に出力(SortedDictionary)されるんですね。なので、hrefより先にdata属性が展開される、と。この辺、どうするんでしょうね。正しくはURLRewriteのOutboud Providerが対応することなんだと思うけど．．．。&lt;/p&gt;
&lt;p&gt;とりあえず、今のところスルー。さーせん。&lt;/p&gt;
&lt;p&gt;ちなみに前回はこれを仮想ディレクトリ配下にデプロイせずに「出来たできた～」と浮かれてて、実はちゃんと消えないっていうダメっぷり。あと、SessionIDだけじゃなくCookielessの場合は認証チケット(F)も匿名ID(A)もURLに含まれるのにSだけ見てて、これまたちゃんと消えないっていうダメダメっぷり。&lt;/p&gt;
&lt;p&gt;今回はちゃんと確認。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-Qn3hR1ctluM/TqtTkUt88uI/AAAAAAAACI8/wvawC3g1hS0/s1600-h/rewrite62.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite6" border="0" alt="rewrite6" src="http://lh6.ggpht.com/-Ime713sXIfQ/TqtTlHy6hDI/AAAAAAAACJE/DiN19Ihbhnc/rewrite6_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ローカルIISのmvc4dpにデプロイ。URLは view-source:http://localhost/mvc4dp/(S(cow2znspocwggst1mah50myt))/ です。これもちゃんとHTML中のURLからはちゃんとSessionIDが消えました！&lt;/p&gt;
&lt;p&gt;でー。この状態で今度はアウトプットキャッシュをOnにします。そのためにHomeControllerに以下の追記。&lt;/p&gt;&lt;pre class="prettyprint"&gt;    [OutputCache(Duration = 60)]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
&lt;/pre&gt;
&lt;p&gt;これをVS実行環境で見てみる。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-DSeiuGnkpxs/TqtTl0sXVZI/AAAAAAAACJM/e0RpBkAXIu8/s1600-h/rewrite72.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite7" border="0" alt="rewrite7" src="http://lh3.ggpht.com/-n1ayb43qKgY/TqtTmladdJI/AAAAAAAACJU/EFkbQX19lW4/rewrite7_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ちゃんと出力キャッシュが効いてる証に、max-ageとExpiresが出てますね。&lt;/p&gt;
&lt;p&gt;ローカル環境でもキャッシュの有無で少しだけ、結果が違いますね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-UICefpTdnBo/TqtTnbVPhoI/AAAAAAAACJg/Yy98xktSaas/s1600-h/rewrite82.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite8" border="0" alt="rewrite8" src="http://lh4.ggpht.com/-A5qZkWQmyxo/TqtToosWaHI/AAAAAAAACJk/yxy9orGIetQ/rewrite8_thumb.png?imgmax=800" width="240" height="227"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh5.ggpht.com/-2620waJ42cs/TqtTpVeIAYI/AAAAAAAACJw/1woeKKoiHDg/s1600-h/rewrite92.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite9" border="0" alt="rewrite9" src="http://lh5.ggpht.com/-a4AOvFEIKyE/TqtTqT6gmrI/AAAAAAAACJ0/ShI0GfM6AyQ/rewrite9_thumb.png?imgmax=800" width="240" height="227"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;少しだけね。で、このキャッシュされてるHTMLにはもちろんSessionIDは含まれてません。&lt;/p&gt;
&lt;p&gt;ココからさらに動的圧縮をOnにするために以下の記述を追加。&lt;/p&gt;&lt;pre class="prettyprint"&gt;    &amp;lt;urlCompression
      doStaticCompression="true"
      doDynamicCompression="true"
      dynamicCompressionBeforeCache="false" /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;と、いいたいところですが、残念ながらこれはこのままでは機能しないんです。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://forums.iis.net/t/1165899.aspx"&gt;URL Rewrite Outbound Rules w/ Compression : The Official Microsoft IIS Site&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;↑ココに書かれてる通り、 レジストリに項目追加が必要です。切ないですね。でも、まぁ、いいでしょう。&lt;/p&gt;&lt;pre class="prettyprint"&gt;Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InetStp\Rewrite]
"LogRewrittenUrlEnabled"=dword:00000000

&lt;/pre&gt;
&lt;p&gt;これをレジストリに追加。&lt;/p&gt;
&lt;p&gt;で、いざ！と思いきや．．．。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-Eyf-EXAYerE/TqtTq354rJI/AAAAAAAACKA/YoEce8AwZzU/s1600-h/rewrite102.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite10" border="0" alt="rewrite10" src="http://lh5.ggpht.com/-Gz-01Yp1xug/TqtTsAh1GQI/AAAAAAAACKI/R5MHyA2hUUI/rewrite10_thumb.png?imgmax=800" width="240" height="227"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ざんねーん。IIS Expressではどうもこのレジストリの値を見てくれないみたいです。対策も特に見つけられなかったです。なのでローカルのIISにデプロイしたほうで試します。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-W4y22I7SUhI/TqtTsy3ujkI/AAAAAAAACKM/cZoGaKMHHpc/s1600-h/rewrite112.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite11" border="0" alt="rewrite11" src="http://lh5.ggpht.com/-0wdN8oMvMxo/TqtTtwuaWQI/AAAAAAAACKU/oMIaQLTvgQE/rewrite11_thumb.png?imgmax=800" width="240" height="227"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh4.ggpht.com/-fCRj_g2sg28/TqtTuSRr_hI/AAAAAAAACKc/Rjhe9QpO6-Q/s1600-h/rewrite123.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rewrite12" border="0" alt="rewrite12" src="http://lh4.ggpht.com/-W-34FeiYj5g/TqtTvNYwrQI/AAAAAAAACKk/3T4rQ9iv7hM/rewrite12_thumb.png?imgmax=800" width="240" height="227"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;たたーん！出力キャッシュも効いてるし、gzipも効いてます。さらにHTML中のSession IDを持ってるURLもなくなってます！Content-Lengthが5.77KBから2.61KB。&lt;/p&gt;
&lt;p&gt;標準(追加モジュールですけどMS謹製)でもココまで出来ました。&lt;/p&gt;
&lt;p&gt;これ以上は独自のResponse Filterを書いてHTML中のURLを操作する方法になるでしょう(もちろんURL RewriteのProviderを実装という手もあるけど、それ書くくらいならFilterのほうが低コストじゃないですかねー)。&lt;/p&gt;
&lt;p&gt;大規模サイトもコレで安心ですね。&lt;/p&gt;
&lt;p&gt;ちなみに、ですけど、すべてのサイトでコレを設定すれば早くなるわけじゃないので用法・用量を守って正しくお使いください。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4644639565505434318?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/fM28sfP36-Q" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/fM28sfP36-Q/urlrewriteoutboundrulesidhtmlurlurl.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-vUy34thkdII/TqtTdNsXvUI/AAAAAAAACH0/OduH47YZbz4/s72-c/rewrite1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/10/urlrewriteoutboundrulesidhtmlurlurl.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-1193244331403636022</guid><pubDate>Sat, 15 Oct 2011 01:49:00 +0000</pubDate><atom:updated>2011-11-19T23:24:51.405+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">C#</category><title>TraceListner into MongoDB</title><description>&lt;p&gt;たまには週間たけはらブログ。&lt;/p&gt; &lt;p&gt;ASP.NETでTraceListner使ってますか？今まで結構仕込んでおいたんだけど、ファイルやイベントログだと扱いにくいなー、なんて思ってませんか。思ってました。融通効かないなー、と。大規模サイトなんかでSQLServerに入れちゃうと、大変なことにナチャウヨ。&lt;/p&gt; &lt;p&gt;そこでMongoDB。みんな大好きMongoDB。ドキュメントの日本語化も着実に進んでるので、英語なんてー、と気にすることもあんまりないでしょう。そーでもないですか？いろいろ可愛いやつですよ！ログデータの保持なんて、もう、得意中の得意です。保持する構造さえちゃんとしておけば、RDBじゃ処理しにくいものもお気楽に扱えます。用途と使い方次第デスけどね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/display/DOCSJP/Home"&gt;Home - Docs-Japanese - 10gen Confluence&lt;/a&gt;&lt;/p&gt; &lt;p&gt;MongoDBって何よ？っていうのは、いろいろ検索してね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/magazine/ee310029.aspx"&gt;MongoDB と NoSQL を試す&lt;/a&gt;&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/magazine/ff714592.aspx"&gt;MongoDB と NoSQL を試す (第 2 部)&lt;/a&gt;&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/magazine/ff798277.aspx"&gt;MongoDB と NoSQL を試す (第 3 部)&lt;/a&gt;&lt;br&gt;&lt;/p&gt; &lt;p&gt;開発環境で超簡単な使い方はコンソールでmongodを起動しておいて、mongoで中身を確認。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-NjvI41qGEgo/TpjmdFLW2CI/AAAAAAAACFY/f5OAuI1bvKs/s1600-h/ml51.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml" border="0" alt="ml" src="http://lh6.ggpht.com/-PeURy5t-LIY/TpjmdoYjEyI/AAAAAAAACFg/K9Zq_0GUFoI/ml_thumb1.png?imgmax=800" width="240" height="131"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;だんだんmongodをサービス起動しておきたくなりますが、最初は単なるプロセス起動。もちろん、内容確認をコンソールのmongoで行うのも、ハッカーみたいな雰囲気あっていいかもしれないけど、そんなの面倒なので実際はGUIのツールを使いましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongovue.com/"&gt;MongoVUE | Gui tools for MongoDB&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-Dwa3nBTrMnM/TpjmeGHVjTI/AAAAAAAACFo/gnr82yLdmpY/s1600-h/ml22.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml2" border="0" alt="ml2" src="http://lh3.ggpht.com/-Kr00k2YmUuI/Tpjmeh8skVI/AAAAAAAACFw/EH9CsuIjkjk/ml2_thumb.png?imgmax=800" width="240" height="179"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;他にもイロイロあるので、気に入ったのを選んで試してみましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/display/DOCS/Admin+UIs"&gt;Admin UIs – MongoDB&lt;/a&gt;&lt;/p&gt; &lt;p&gt;このMongoDBにTraceListnerからmessageを保存するようにしちゃいましょー！&lt;/p&gt; &lt;p&gt;開発するならHTTPでのREST操作(MongoDBに最初からあります)よりも、Driverを使った開発のほうが楽チンぽんです。前までは、いろいろ使い勝手の問題もあったりとかしたけど、いまとなっては標準公開されてるもので十分です。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/display/DOCS/CSharp+Language+Center"&gt;CSharp Language Center – MongoDB&lt;/a&gt;&lt;br&gt;&lt;a href="http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial"&gt;CSharp Driver Tutorial - MongoDB&lt;/a&gt;&lt;/p&gt; &lt;p&gt;英語かよ！どんまい。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.mongodb.org/pages/viewpage.action?pageId=5079208"&gt;マニュアル - Docs-Japanese - 10gen Confluence&lt;/a&gt;&lt;/p&gt; &lt;p&gt;なんにせよNuGetで取得できるのが便利なところです。&lt;/p&gt; &lt;p&gt;&lt;a href="http://nuget.org/List/Packages/mongocsharpdriver"&gt;Official MongoDB C# driver - 1.2 : NuGet gallery&lt;/a&gt;&lt;/p&gt; &lt;p&gt;TraceListnerって自分で用意したことなかったんだけど、TraceListnerクラス派生でいいってことなので、お手軽ですね。必須なoverrideも2個だけ。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public override void Write(string message)
{
  // ...
}

public override void WriteLine(string message)
{
  // ...
}
&lt;/pre&gt;
&lt;p&gt;やれそうですね。&lt;/p&gt;
&lt;p&gt;えいやっ！&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System.Configuration;
using System.Diagnostics;
using MongoDB.Bson;
using MongoDB.Driver;

namespace MongoListner
{
  public class MongoDbTraceListner : TraceListener
  {
    private readonly MongoServer _server;
    private readonly MongoDatabase _database;
    private readonly string _collectionName;

    public MongoDbTraceListner() : this("TraceData") { }
    public MongoDbTraceListner(string initializeData)
    {
      _collectionName = initializeData;
      var serverName = ConfigurationManager.AppSettings["MongoDb:Server"];
      var databaseName = ConfigurationManager.AppSettings["MongoDb:Database"];

      if (string.IsNullOrEmpty(serverName))
          serverName = "mongodb://localhost";

      if (string.IsNullOrEmpty(databaseName))
          databaseName = "TraceListner";

      _server = MongoServer.Create(serverName);
      _database = _server.GetDatabase(databaseName);
    }

    private void Insert(BsonDocument document)
    {
      var collection = _database.GetCollection(_collectionName);
      collection.Insert(document);
    }

    private void InternalWrite(string message)
    {
      var document = new BsonDocument {{"Message", message}};
      Insert(document);
    }

    private void InternalWriteObject(object o)
    {
      var document = new BsonDocument();
      
      var type = o.GetType();
      foreach(var prop in type.GetProperties())
      {
          document.Add(prop.Name, prop.GetValue(o,new object[]{}).ToString());
      }
      Insert(document);
    }

    public override void Write(string message)
    {
      InternalWrite(message);
    }

    public override void WriteLine(string message)
    {
      InternalWrite(message);
    }

    public override void WriteLine(object o)
    {
      var type = o.GetType();
      if (type.Name.StartsWith("&amp;lt;&amp;gt;") &amp;amp;&amp;amp; type.Name.Contains("AnonymousType"))
      {
          InternalWriteObject(o);
          return;
      }
      InternalWrite(o.ToString());
    }
  }
}&lt;/pre&gt;
&lt;p&gt;できたー。&lt;/p&gt;
&lt;p&gt;後は、web.configに書いて使えるようにするだけですね！&lt;/p&gt;&lt;pre class="prettyprint"&gt;  &amp;lt;appSettings&amp;gt;
    &amp;lt;add key="webpages:Version" value="1.0.0.0"/&amp;gt;
    &amp;lt;add key="ClientValidationEnabled" value="true"/&amp;gt;
    &amp;lt;add key="UnobtrusiveJavaScriptEnabled" value="true"/&amp;gt;
    
    &amp;lt;add key="MongoDb:Server" value="mongodb://localhost"/&amp;gt;
    &amp;lt;add key="MongoDb:Database" value="TraceListner"/&amp;gt;
  &amp;lt;/appSettings&amp;gt;&lt;/pre&gt;&lt;pre class="prettyprint"&gt;&amp;lt;system.diagnostics&amp;gt;
  &amp;lt;trace autoflush="false" indentsize="4"&amp;gt;
    &amp;lt;listeners&amp;gt;
      &amp;lt;add name="mongoListener" 
           type="MongoListner.MongoDbTraceListner, MongoListner"
           initializeData="MyTrace" /&amp;gt;
      &amp;lt;remove name="Default" /&amp;gt;
    &amp;lt;/listeners&amp;gt;
  &amp;lt;/trace&amp;gt;
&amp;lt;/system.diagnostics&amp;gt;
&lt;/pre&gt;&lt;pre class="prettyprint"&gt;  &amp;lt;system.web&amp;gt;
    &amp;lt;trace enabled="true"/&amp;gt;
&lt;/pre&gt;
&lt;p&gt;あとはSystem.Diagnostics.Trace.Write/WriteLineです！すでにたくさん仕込んでいる場合にはコレでOK。&lt;/p&gt;
&lt;p&gt;ASP.NET MVC3標準プロジェクトを作成して、MongoDbTraceListnerクラスを作成し、HomeControllerのIndexアクションにTraceを書いてみましょう。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-PRitdmz9gjM/TpjmfZ6aniI/AAAAAAAACF4/_hrwRTQ8jLk/s1600-h/ml42.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml4" border="0" alt="ml4" src="http://lh4.ggpht.com/-mfSVvFLSdQ4/Tpjmf_SIQjI/AAAAAAAACGA/w9FnFM4HbrQ/ml4_thumb.png?imgmax=800" width="240" height="151"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;わーお！素敵！！&lt;/p&gt;
&lt;p&gt;MongoDBが違うマシンだったり(本番はそうしましょう)、Database名を変更したいときはappSettingsの値を変えてね。コレクション(テーブル相当)の名前を変えたかったらtrace/listnersのinitializeDataで指定してね。&lt;/p&gt;
&lt;p&gt;ちなみに、これだけだとつまんないので、ここから少し拡張してパフォーマンス計測してみましょう。まずはIHttpModuleを実装して、リクエストの処理時間を計測するようにしてみます。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://code.google.com/p/mvc-mini-profiler/"&gt;mvc-mini-profiler - A simple but effective mini-profiler for ASP.NET and WCF - Google Project Hosting&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;↑パフォーマンス計測ならこんな素敵なもの(&lt;a href="http://nugit.org/"&gt;NuGit.org&lt;/a&gt;で実物見れますよ～)もありますが．．．。これから新規ならこっちのほうが．．．。いや、言うまい。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-gOyMa4JbbQM/TpjmgfHIg1I/AAAAAAAACGI/a6xEfUTLd60/s1600-h/ml35.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml3" border="0" alt="ml3" src="http://lh5.ggpht.com/-HZE0BOmVi0M/Tpjmg5CpbxI/AAAAAAAACGQ/FwMCi9YDpvM/ml3_thumb1.png?imgmax=800" width="240" height="161"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;どりゃ！&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;

namespace MongoListner
{
  public class PerformanceTraceModule : IHttpModule
  {
    private string ItemKey = "_mongoDbTraceStart";
    private readonly string _serverName = "";

    public PerformanceTraceModule()
    {
      _serverName = Environment.MachineName;
    }

    public void Init(HttpApplication context)
    {
      context.BeginRequest += new EventHandler(context_BeginRequest);
      context.EndRequest += new EventHandler(context_EndRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
      var httpContext = (sender as HttpApplication).Context;
      var startTime = httpContext.Items[ItemKey] = DateTime.Now;
    }

    void context_EndRequest(object sender, EventArgs e)
    {
      var httpContext = (sender as HttpApplication).Context;
      var startTime = (DateTime)httpContext.Items[ItemKey];

      Trace.WriteLine(new
      {
        Server = _serverName,
        RequestAt = startTime,
        Method = httpContext.Request.HttpMethod,
        Status = httpContext.Response.StatusCode,
        RawUrl = httpContext.Request.Url.ToString(),
        Milliseconds = (DateTime.Now - startTime).Milliseconds
      });
    }

    public void Dispose()
    {
    }
  }
}&lt;/pre&gt;
&lt;p&gt;これを利用するためにsystem.webServer/modulesに登録します。&lt;/p&gt;&lt;pre class="prettyprint"&gt;  &amp;lt;system.webServer&amp;gt;
    &amp;lt;validation validateIntegratedModeConfiguration="false"/&amp;gt;
    &amp;lt;modules runAllManagedModulesForAllRequests="true"&amp;gt;
      &amp;lt;add name="mongoListner" 
           type="MongoListner.PerformanceTraceModule, MongoListner" 
           preCondition="integratedMode"/&amp;gt;
    &amp;lt;/modules&amp;gt;
  &amp;lt;/system.webServer&amp;gt;
&lt;/pre&gt;
&lt;p&gt;実行してみます。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-pHqNfsPgC1A/TpjmhR-rrVI/AAAAAAAACGY/qvHpYD0iSyg/s1600-h/ml52.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml5" border="0" alt="ml5" src="http://lh4.ggpht.com/-D08tZCHUw7E/Tpjmh9FYp7I/AAAAAAAACGg/nUTycMMqq0E/ml5_thumb.png?imgmax=800" width="240" height="156"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;BeginRequestからEndRequestの間を計測するものですが490msって．．．。遅すぎ！&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-PtlsM-EsKSM/TpjmidFoDMI/AAAAAAAACGo/JYtf9xgQNKU/s1600-h/ml62.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml6" border="0" alt="ml6" src="http://lh3.ggpht.com/-GV8UQ4dZ524/Tpjmi-EztiI/AAAAAAAACGw/c2hgb_mu8-o/ml6_thumb.png?imgmax=800" width="240" height="168"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;と、思ったらF5リロードの2回目は5ms。そーだろそーだろ。ん？よく見たらStatusとMillisecondsがstringになってるー。TraceListnerのInternalWriteObjectでToStringしてたね。失敬。そこは修正しましょう。書きながら作る、作りながら書く！&lt;/p&gt;&lt;pre class="prettyprint"&gt;private void InternalWriteObject(object o)
{
  var document = new BsonDocument();
  
  var type = o.GetType();
  foreach(var prop in type.GetProperties())
  {
    var value = BsonValue.Create(prop.GetValue(o,new object[]{}));
    document.Add(prop.Name, value);
  }
  Insert(document);
}
&lt;/pre&gt;
&lt;p&gt;これでちゃんと型どおり。BsonValueにはいろいろあるのでドキュメント参照してみてください。&lt;/p&gt;
&lt;p&gt;このHttpModuleがあればすべてのリクエストの処理時間が計測できますね！アクセスログのTimeTakenとダダカブリ。どんまい。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.microsoft.com/japan/windowsserver2003/community/centers/iis/iis6_faq.mspx"&gt;IIS 6.0 ログ ファイルの Time Taken フィールドは何を表し、何を意味していますか。 : IIS 6.0 についてよく寄せられる質問&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ASP.NET MVCならActionFilter属性を使ってControllerでの処理時間とViewの処理時間をそれぞれ別で計測できてなお嬉しいはず。&lt;/p&gt;
&lt;p&gt;そいやっ！&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System;
using System.Diagnostics;
using System.Web.Mvc;

namespace MongoListner
{
  public class PerformanceTraceAttribute : ActionFilterAttribute
  {
    private string ItemKey = "_mongoDbFilterTraceStart";
    private readonly string _serverName = "";

    public PerformanceTraceAttribute()
    {
      _serverName = Environment.MachineName;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      filterContext.HttpContext.Items[ItemKey] = DateTime.Now;

      base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
      var startTime = (DateTime)filterContext.HttpContext.Items[ItemKey];

      Trace.WriteLine(new
      {
          Server = _serverName,
          ProcessAt = DateTime.Now,
          RawUrl = filterContext.HttpContext.Request.Url.ToString(),
          Method = filterContext.HttpContext.Request.HttpMethod,
          Controller = filterContext.RouteData.GetRequiredString("controller"),
          Action = filterContext.RouteData.GetRequiredString("action"),
          Milliseconds = (DateTime.Now - startTime).Milliseconds
      });

      base.OnActionExecuted(filterContext);
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
      filterContext.HttpContext.Items[ItemKey] = DateTime.Now;

      base.OnResultExecuting(filterContext);
    }

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
      var startTime = (DateTime)filterContext.HttpContext.Items[ItemKey];
      var viewName = (string) null;
      if (filterContext.Result is ViewResult)
      {
        viewName = (filterContext.Result as ViewResult).ViewName;
      }
      Trace.WriteLine(new
      {
        Server = _serverName,
        ProcessAt = DateTime.Now,
        RawUrl = filterContext.HttpContext.Request.Url.ToString(),
        Result = viewName ?? filterContext.Result.GetType().Name,
        Controller = filterContext.RouteData.GetRequiredString("controller"),
        Action = filterContext.RouteData.GetRequiredString("action"),
        Milliseconds = (DateTime.Now - startTime).Milliseconds
      });

      base.OnResultExecuted(filterContext);
    }
  }
}&lt;/pre&gt;
&lt;p&gt;これを有効にするためにGlobal Filterに追加しておきましょう。&lt;/p&gt;
&lt;p&gt;あとは実行するだけ！&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-0t9JbddyqSg/TpjmjaN-YsI/AAAAAAAACG4/tW5S9h4zXtA/s1600-h/ml7%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml7" border="0" alt="ml7" src="http://lh4.ggpht.com/-6ACyE5S5IxM/Tpjmjy1csTI/AAAAAAAACHA/nwt6equThmU/ml7_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ズームして見てね。&lt;/p&gt;
&lt;p&gt;Controllerでの実行時間が223ms、ActionResultの実行時間が180ms、HttpModuleでの計測時間が497ms。差が94msありますね。そんなもんでしょう。ちなみにコレが初回実行時の計測で、2回目は↓。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-s418JqvKcTY/TpjmkZdVTCI/AAAAAAAACHI/rfiy113FHTQ/s1600-h/ml8%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml8" border="0" alt="ml8" src="http://lh6.ggpht.com/-vGYs6ZCBzSo/TpjmlbUEllI/AAAAAAAACHQ/zEeWplbVgnM/ml8_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Controllerでの実行時間が0ms、ActionResultの実行時間が0ms、HttpModuleでの計測時間が10ms。差が10ms。これまた、そんなもんでしょう。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-9A04xJKQ26E/Tpjml2KfA_I/AAAAAAAACHY/l7kodYh9Hk0/s1600-h/ml9%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ml9" border="0" alt="ml9" src="http://lh6.ggpht.com/-Ay7HaRRNI_g/TpjmmQtI8yI/AAAAAAAACHg/iIHZbIcAiJs/ml9_thumb.png?imgmax=800" width="240" height="161"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;こんな感じです！楽しいですね！&lt;/p&gt;
&lt;p&gt;途中、MongoDBに入れるDocumentのカラムを変更したりしてるけど、MongoDB側へは何も手を加える必要がないんです。ドキュメント単位(テーブルなら行単位)にカラム構成が変更されてもお構いなしです。ちなみにサーバーさえいればDatabaseもCollectionも初回アクセス時に勝手に作られるので準備は不要。この手軽さと、レスポンス性能の高さがMongoDBの魅力です。&lt;/p&gt;
&lt;p&gt;今回のプロジェクト一式は↓こちら。ローカルにMongoDBさえ入っていればそのまま動くはず。&lt;/p&gt;
&lt;p&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/SampleProjects/MongoListner.zip?cid=df0c51db6ad31f29&amp;amp;sc=documents" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;※ファイルを小さくするために、packages/mongocsharpdriver.1.2を消してます。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-1193244331403636022?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/KBOKGdbqHvw" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/KBOKGdbqHvw/tracelistner-into-mongodb.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-PeURy5t-LIY/TpjmdoYjEyI/AAAAAAAACFg/K9Zq_0GUFoI/s72-c/ml_thumb1.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/10/tracelistner-into-mongodb.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-7417565729135223546</guid><pubDate>Mon, 10 Oct 2011 15:43:00 +0000</pubDate><atom:updated>2011-10-11T00:43:39.914+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>AllowHtmlの深淵</title><description>&lt;p&gt;月刊たけはらブログ。と、なってしまいましたね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://weblogs.asp.net/imranbaloch/archive/2011/02/19/understanding-request-validation-in-asp-net-mvc-3.aspx"&gt;Understanding Request Validation in ASP.NET MVC 3&lt;/a&gt;&lt;/p&gt; &lt;p&gt;唐突ですが、↑随分前のこのエントリ、ずっと気になってたんです。ValidateInputAttributeってあるじゃないですか、Ver1の時から。これを指定したControllerやActionは不正なリクエスト文字列が含まれると&lt;a href="http://msdn.microsoft.com/ja-jp/library/system.web.httprequestvalidationexception(v=vs.80).aspx"&gt;HttpRequestValidationException&lt;/a&gt;をthrowするやつです。&lt;/p&gt; &lt;p&gt;これ、もともとASPXのPageディレクティブのValidateRequestとしても使われてますね。Pageディレクティブですよ。ココ重要。&lt;/p&gt; &lt;p&gt;MVC3になってからAllowHtmlAttributeって言うモデルプロパティに指定できるやつが追加されたじゃないですか。これ、とても不思議に思ってたんです。でも、まぁいっか、思ったように動くし、と、見て見ぬふりしてたんですけど、どーも、気持ち悪くてですね。&lt;/p&gt; &lt;p&gt;どういうことかというと、Pageディレクティブだと&lt;a href="http://msdn.microsoft.com/ja-jp/library/system.web.httprequest.validateinput(v=VS.80).aspx"&gt;HttpRequest.ValidateInput()&lt;/a&gt;に直接連携するというのはすんなり受け入れられるけど、それってリクエストコンテキスト単位のチェック(Form,QueryString,Cookieが送信されてきたリクエスト自体)で、個々の要素っていうか値単位のチェックなわけじゃない、っていう仕組みじゃないですか。なのでAllowHtmlってソレまでの仕組みとは全然違うものを実装してるということになりましょう。そうなると、どういう仕組で実装されてるのかキニナル。気になる。&lt;/p&gt; &lt;p&gt;で、先のブログのエントリなんですけど、正直かなり難しそうだなー、と思ってずっと敬遠してました。&lt;/p&gt; &lt;p&gt;さっそく原点、ASP.NET MVC1のControllerActionInvoker見てみます。&lt;/p&gt;&lt;pre class="prettyprint"&gt;[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "rawUrl",
  Justification = "We only care about the property getter's side effects, not the returned value.")]
private static void ValidateRequest(HttpRequestBase request) {
  // DevDiv 214040: Enable Request Validation by default for all controller requests
  // 
  // Note that we grab the Request's RawUrl to force it to be validated. Calling ValidateInput()
  // doesn't actually validate anything. It just sets flags indicating that on the next usage of
  // certain inputs that they should be validated. We special case RawUrl because the URL has already
  // been consumed by routing and thus might contain dangerous data. By forcing the RawUrl to be
  // re-read we're making sure that it gets validated by ASP.NET.

  request.ValidateInput();
  string rawUrl = request.RawUrl;
}
&lt;/pre&gt;
&lt;p&gt;HttpRequestBaseのValidateInputを呼んで、RawUrlにアクセスするだけのシンプルな実装ですね。コメントから苦労が垣間見れます。&lt;/p&gt;
&lt;p&gt;ASP.NET MVC3の同じ部分を見てみます。&lt;/p&gt;&lt;pre class="prettyprint"&gt;internal static void ValidateRequest(ControllerContext controllerContext) {
  if (controllerContext.IsChildAction) {
      return;
  }

  // DevDiv 214040: Enable Request Validation by default for all controller requests
  // 
  // Earlier versions of this method dereferenced Request.RawUrl to force validation of
  // that field. This was necessary for Routing before ASP.NET v4, which read the incoming
  // path from RawUrl. Request validation has been moved earlier in the pipeline by default and
  // routing no longer consumes this property, so we don't have to either.

  ValidationUtility.EnableDynamicValidation(HttpContext.Current);
  controllerContext.HttpContext.Request.ValidateInput();
}
&lt;/pre&gt;
&lt;p&gt;微妙に違いますね。Microsoft.Web.Infrastructure.DynamicValidationHelperのValidationUtility.EnableDynamicValidationですね。誰や！？この旅、ここから長いです。長い割に身が無いです。できることはわかってるんだから。&lt;/p&gt;
&lt;p&gt;先のブログエントリを、ちらっと見てるという前提で話を進めますが、MVC3だとHttpRequestのForm,QueryStringへのアクセスでもHttpRequestValidationException発生しますね。&amp;lt;httpRuntime requestValidationMode="2.0"/&amp;gt;はweb.configに入れてません。入れてないとASP.NET4ではデフォルトで全てのコレクションに対する検証がOnになるからです。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.asp.net/LEARN/whitepapers/aspnet4/breaking-changes#0.1__Toc256770147"&gt;ASP.NET 4 Breaking Changes - ASP.NET Request Validation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ASP.NET 2/3.0/3.5と同じ挙動にしたいときだけ。&amp;lt;httpRuntime requestValidationMode="2.0"/&amp;gt;を指定しましょう。必要ないと思いますけど。んで、なんでここに手を入れてるのかというと、検証処理自体細かく制御できるように拡張ポイントを追加したからですね。requestValidationModeともう一つrequestValidationTypeというのも指定できるようになってますが、こっちで検証クラス(検証とずっと言ってるけど通常の入力検証じゃなくてリクエスト検証のことなので誤解しないでね！)を指定してカスタム出来るようになってます。&lt;/p&gt;
&lt;p&gt;標準テンプレートのまま１つソリューションを作成します。HomeControllerのIndexで以下のようにQueryStringにアクセスするとします。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public ActionResult Index()
{
  var p1 = Request.QueryString["p1"];

  ViewBag.Message = "Welcome to ASP.NET MVC!";

  return View();
}
&lt;/pre&gt;
&lt;p&gt;なんの変哲もないコードですね。これを実行してブラウザのアドレス欄に”?p1=&amp;lt;a&amp;gt;a&amp;lt;/a&amp;gt;”なんていうのを足してアクセスしなおすと、例のエラーでます。そりゃそうです。The XSSです。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-kgstEstT9fE/TpMSezqPE8I/AAAAAAAACD8/60iqF01xEVk/s1600-h/rv12.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv1" border="0" alt="rv1" src="http://lh5.ggpht.com/-AkZcO6Erwck/TpMSfiFblgI/AAAAAAAACEA/C4lHuUcSepY/rv1_thumb.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-7Ihl4Q0OoRM/TpMSgPNFAPI/AAAAAAAACEE/EH1K66vyD0I/s1600-h/rv22.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv2" border="0" alt="rv2" src="http://lh5.ggpht.com/-5J2CRzENNHs/TpMSgkkHvsI/AAAAAAAACEI/pjG9UPtZ4E4/rv2_thumb.png?imgmax=800" width="240" height="199"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh6.ggpht.com/-y_mnx7_zu-U/TpMShE5a-EI/AAAAAAAACEM/0WZgxyy5uSs/s1600-h/rv32.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv3" border="0" alt="rv3" src="http://lh4.ggpht.com/-bKNLEpKhkWU/TpMShyB5g5I/AAAAAAAACEQ/emdTLQIfT4U/rv3_thumb.png?imgmax=800" width="240" height="199"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;今度はそこに↓こんなずるいクラスを追加してrequestValidationTypeに指定。&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System.Web;
using System.Web.Util;

namespace ReqValidate.MVC3
{
  public class PassRequestValidator : RequestValidator
  {
    protected override bool IsValidRequestString(
        HttpContext context,
        string value,
        RequestValidationSource requestValidationSource,
        string collectionKey,
        out int validationFailureIndex)
    {
      validationFailureIndex = -1;
      return true;
    }
  }
}&lt;/pre&gt;
&lt;p&gt;ひどい。&lt;/p&gt;
&lt;p&gt;&amp;lt;httpRuntime &lt;br&gt;&amp;nbsp; requestValidationType="ReqValidate.MVC3.PassRequestValidator"/&amp;gt;&lt;/p&gt;
&lt;p&gt;このまま先のURLにもう一度アクセス。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-LQ5gNJIYk_A/TpMSievSrGI/AAAAAAAACEU/ivWFJBdBVvE/s1600-h/rv42.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv4" border="0" alt="rv4" src="http://lh4.ggpht.com/-Mab4zabRt6k/TpMSi2ndQGI/AAAAAAAACEc/kA0alZq4HG8/rv4_thumb.png?imgmax=800" width="240" height="199"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;同じようにダメそうなQueryStringを指定してるのに、今度はエラーになりませんでした。より厳しい条件を指定したいときなんかはカスタムすればいいですね。同じ仕組でRawUrlのチェックで許可したい文字を増やす事や、拒否したい文字を減らすこともできるでしょう。残念ながら標準でできるようになってるので、そんなことする意味は無いですけどね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.asp.net/learn/whitepapers/aspnet4#0.2__Toc253429244"&gt;ASP.NET 4 and Visual Studio 2010 Web Development Overview - Expanding the Range of Allowable URLs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ただ、この辺突き詰めていけば、AllowHtmlにたどり着くのかというと、そうでもない、っていうね。なんでしょーね。この話、必要なかったですね。&lt;/p&gt;
&lt;p&gt;話を戻すと、requestValidationModeが2.0の時と、4.0(デフォルト)の時での挙動の違いとして、チェックが有効になるタイミングの違いがあるようです。2.0だとBegineRequestではまだ有効になってなくて、4.0では有効になってます。&lt;/p&gt;
&lt;p&gt;Global.asaxでブレークを仕掛けるとわかります。なるほど。Request.ValidateInput()でチェックフラグがオンになるので、以降のForm/QueryStringへのアクセスでチェックがかかるという仕組みです。&lt;/p&gt;
&lt;p&gt;requestValidationMode=”2.0”の時。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-kLswzc1AaBY/TpMSjQ5OmEI/AAAAAAAACEg/qd7rxjKqejU/s1600-h/rv55.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv5" border="0" alt="rv5" src="http://lh4.ggpht.com/-ih1Ud_fNU4k/TpMSj1JxXbI/AAAAAAAACEk/Z3qKN9jH-zo/rv5_thumb1.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Microsoft.Web.Infrastructure.DynamicValidationHelper.ValidationUtility.IsValidationEnabled(HttpContext.Current)で確認しましょう。&lt;/p&gt;
&lt;p&gt;falseです。&lt;/p&gt;
&lt;p&gt;なので、このタイミングで上記スクリーンショットのQueryStringを参照すると、XSSな値が入っていても、このタイミングではエラーとなりません。&lt;/p&gt;
&lt;p&gt;でも、Request.ValidateInput()でマークフラグをセットすると、同じものが今度は例外となります。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-nNOrUSwNXxA/TpMSkftfW3I/AAAAAAAACEo/HR7PWJ9Qasc/s1600-h/rv62.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv6" border="0" alt="rv6" src="http://lh4.ggpht.com/-5s1I6PzeO8Q/TpMSlAKcqlI/AAAAAAAACEs/V6MN0o7P8_U/rv6_thumb.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;続いて、同じコードでValidateInput()を呼ばずに、requestValidationMode=”4.0”の時。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-ibKEoJT4DZ4/TpMSltqCuvI/AAAAAAAACEw/k-hUDPFWs1k/s1600-h/rv72.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv7" border="0" alt="rv7" src="http://lh6.ggpht.com/-FPdiwL7f-9c/TpMSmCema6I/AAAAAAAACE0/tHat99vtvrY/rv7_thumb.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;IsValidationEnabledもtrueで、例外も起きます。&lt;/p&gt;
&lt;p&gt;面白いのは、同じコードを続けて実行すると二回目以降は例外が発生しないところです。これは興味深い挙動ですね。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-n-T77Df-KTU/TpMSmkC4IsI/AAAAAAAACE4/EbN3ApAgIHQ/s1600-h/rv82.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv8" border="0" alt="rv8" src="http://lh3.ggpht.com/-_iD5M9NXUQk/TpMSnOOvUuI/AAAAAAAACE8/OGBE9SdZ8GY/rv8_thumb.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不思議な仕様ですねー。だって、この状態でも、Request.Unvlidated().QueryStringでオリジナルを取得することはできるんだから。過去の遺産だったりするのかなー。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-lMNsgqFWu5I/TpMSnmEIE3I/AAAAAAAACFA/bWewKFA65TM/s1600-h/rv92.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="rv9" border="0" alt="rv9" src="http://lh5.ggpht.com/-MTakT-4443I/TpMSoOIB9_I/AAAAAAAACFE/xNrWHKWJhls/rv9_thumb.png?imgmax=800" width="240" height="194"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;やっと、本題。AllowHtmlを成立させるために必要なのはモデルのプロパティ名毎に検証を実施しないとダメですが、ここまでの流れで分かる通り、マッピング対象のコレクションにアクセスした段階で検証が発動する仕組みなので、このまますんなり行くとは思えません。がしかし、Reques.QueryStringやRequest.Formのアクセスは要素に関係なくエラーになるにもかかわらず、Actionへの引数となるマッピングの場合はマッピングが発生しない要素のにたいする検証は実施されてません。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh6.ggpht.com/-uF8azMscWzc/TpMSpEwC_4I/AAAAAAAACFI/QNBoEaGT8BA/s1600-h/rv10%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="rv10" border="0" alt="rv10" src="http://lh3.ggpht.com/-AXo0JEM8LVM/TpMSpux9BrI/AAAAAAAACFM/usdthe34SxE/rv10_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh3.ggpht.com/-zBStZOAsS3Q/TpMSqPrTkeI/AAAAAAAACFQ/UY-tYHsdiJw/s1600-h/rv11%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="rv11" border="0" alt="rv11" src="http://lh6.ggpht.com/-Y-bDpNAXC2I/TpMSqqmALuI/AAAAAAAACFU/AbKslfMRgks/rv11_thumb.png?imgmax=800" width="240" height="151"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;どうやって実現してるんでしょうね。Form/QueryStringへのアクセスはすべて引っ掛けられてるなら途中介入するなんてできそうな気がしないです。&lt;/p&gt;
&lt;p&gt;エントリを読み進めるとどうやら、コレクションが内部で保持しているArrayListとHashtableを置き換えてるようです。何と置き換えてるかはエントリの通り、LazyValidationArrayListとLazyValidationHashtableです。Microsoft.Web.Infrastracture.DynamicValidationHelper配下。だからといって、コレでも要素アクセスに限り検証し、プロパティマッピングでは検証しないというルールに繋がらないです。ふむむー。&lt;/p&gt;
&lt;p&gt;改めてMVC3のソースを確認すると、そこにはIUnvalidatedRequestValuesと見慣れないインターフェースを使ったValueProviderたちがいました。あれれ？これの実装クラスがUnvalidatedRequestValuesクラスのインスタンスを内包したクラス。UnvalidatedRequestValuesといえば、Request.Unvalidated()で取得できる検証スキップコレクション。&lt;/p&gt;
&lt;p&gt;さらに各種ValueProviderはNameValueCollectionValueProviderを派生したもので、IUnvalidatedValueProviderを実装してます。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public interface IUnvalidatedValueProvider : IValueProvider {
  ValueProviderResult GetValue(string key, bool skipValidation);
}
&lt;/pre&gt;
&lt;p&gt;ははーん。見えてきましたね。MVC3からのValueProviderたちはコレを実装した形になっているので、マッピング対象の値をForm/QueryString等のコレクションから取得する際に、こっちのGetValueを呼び出すことで判断してるんですね。この中を少し見てみるとValueProviderResultPlaceholderを各ValueProviderからの戻り値とする際に、検証コレクションと未検証コレクションを切り替えて返す。&lt;/p&gt;
&lt;p&gt;検証されるべきコレクションはインフラストラクチャの値をそのまま利用し、検証をスキップしたい時のコレクションはUnvalidatedRequestValuesを利用する。それをModelMetadataのRequestValidationEnabledから判定(AllowHtmlはここをセットするための属性クラス)。すっきりした！&lt;/p&gt;
&lt;p&gt;途中出てきたLazyValidationArrayList/LazyValidationHashtableがインフラストラクチャが実施する検証を遅延してくれてるんだろーか。その辺はリフレクションとかしまくっててわかりにくし。FormやQueryStringにアクセスするとエラーが起きるのはそのままだから、なにが遅延かわかりにくいです。どーしてなーん？&lt;/p&gt;
&lt;p&gt;こんな感じの実装になってるからAllowHtmlが成立するというのがわかったので良しとします。こうすることでWebPagesとも検証コードが同一のものにできるわけですね(WebPageHttpHandler.ProcessRequestInternalでEnableDynamicValidation)。この辺はSystem.Web.UI.Page派生じゃないものに対しても正しく検証を行うために必要なところですよね。Pageディレクティブないし。&lt;/p&gt;
&lt;p&gt;ちょっとスッキリ。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-7417565729135223546?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/mWR5ENEc9sU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/mWR5ENEc9sU/allowhtml.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-AkZcO6Erwck/TpMSfiFblgI/AAAAAAAACEA/C4lHuUcSepY/s72-c/rv1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/10/allowhtml.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-8272056250555076204</guid><pubDate>Fri, 23 Sep 2011 02:21:00 +0000</pubDate><atom:updated>2011-09-23T11:21:56.572+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">EF</category><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">C#</category><title>DB Migration on EF4 alpha 3</title><description>&lt;p&gt;&lt;a href="http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-released.aspx"&gt;Code First Migrations: Alpha 3 Released - ADO.NET team blog - Site Home - MSDN Blogs&lt;/a&gt; &lt;/p&gt; &lt;p&gt;EF.Migrationのアルファ3でた。前回はSQL CompactとSQL Expressへの依存が入ってたみたいで、ちゃんと動かせなかったけど、今回これは削除されたって。これでやっと試せる。 &lt;p&gt;せっかくなので &lt;ul&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-no-magic-walkthrough.aspx"&gt;Code First Migrations: Alpha 3 ‘No-Magic’ Walkthrough&lt;/a&gt; &lt;li&gt;&lt;a href="http://blogs.msdn.com/b/adonet/archive/2011/09/21/code-first-migrations-alpha-3-with-magic-walkthrough-automatic-migrations.aspx"&gt;Code First Migrations: Alpha 3 ‘With-Magic’ Walkthrough (Automatic Migrations)&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;↑この2つを試してみよう。 &lt;p&gt;そのまえに！すでにEF4.1のパッケージをインストールしている場合は、削除するか最新版に更新しましょう。今回は削除したけど。これが残ってるとマイグレーションできないので気をつけてね。 &lt;p&gt;まずはNoMagic(AutomaticMigrationsEnabled = false;)。書かれてる通りの操作をするとあれよあれよと、DBが更新されていく。 &lt;ol&gt; &lt;li&gt;データベースが作られてBlogsテーブルが出来る。  &lt;li&gt;Blogsにカラムとユニークインデックスの追加、Postsテーブルが追加。  &lt;li&gt;Postsにカラム追加しつつ、カスタムのUpdate文を実行。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;※黄色い箇所は手書きしましょう。 &lt;p&gt;既存のデータベースにたいしての、途中からのマイグレーションはできないってさ。 &lt;p&gt;どうやってトラッキングしてるのかというとCodeFirstではEdmMetadataユーザーテーブルだったのが、__MigrationHistoryシステムテーブルに引っ越して管理されてます。 &lt;p&gt;Add-Migrationで変更分のコードを自動生成。差分は手で入力。DbMigrationの派生クラスが作成されるので、いかようにもいじれますね。ただ、どんなメソッドがあるんだろね。ドキュメント希望。どっかにあるけ？ &lt;p&gt;Update-DatabaseでデータベースにSQL発行。-Verbose付けとくと発行されるSQLが実行時に確認できます。ただ、手元の環境では-Scriptがエラーで動きませんでした。SQLをファイルにしてくれるオプションのはずなんだけどなー．．．。 &lt;p&gt;続いてMagic(AutomaticMigrationsEnabled = true;)。何がマジックかというと、Add-MigrationしないでいきなりUpdate-Database。すると、ちゃんとMigrationHistoryにAutomaticMigrationとしてレコードが作られて、テーブルも作成されている。凄いね。 &lt;p&gt;カスタムSQLの実行や、カラム初期値を指定する場合なんかだと、コードに指定したメタから生成される情報からだと足りないから、手書きで足してたんだけど、Automaticだとどうするかというと、これがまた単純にAdd-Migrationを呼んで、空のマイグレーションファイルを作成(Up/Downが空)しておいて、そこに書きこんでいくだけ。適用はUpdate－Databaseで、ドーン！ &lt;p&gt;面白いね。しかもコレ、途中経過のDbMigrationがいないのに、ちゃんとDownしていく。なんで？？-Verboseつけて実行すると、ちゃんと差分カラムの削除だけ実行してる．．．。まさにマジック！気持ち悪ーい。 &lt;p&gt;でも、多分AutoMigrationはオススメしないかなー。何をやったのかの履歴が目で見てわからないもん。バージョン管理デキナイヨ！コードでするからやらないよ！？なのかな。 &lt;p&gt;途中までTumblrで書いてたけど中途半端に長くなったので、こっちに移動しました。なのでスクリーンショットもなくてすいません。ただ！やればわかる！書いてることなぞるだけだし。MVC4のMobileチュートリアルよりよっぽどミスしないよ。 &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-0yjSXJ9ofmM/TnvtQSI9yXI/AAAAAAAACD0/rCfO9LAcrhI/s1600-h/Ef41A3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Ef41A3" border="0" alt="Ef41A3" src="http://lh4.ggpht.com/-VdPNedqHiRA/TnvtQ9lxLdI/AAAAAAAACD4/zTnUhVqgr7g/Ef41A3_thumb.png?imgmax=800" width="240" height="184"&gt;&lt;/a&gt; &lt;p&gt;せっかくなのでお試しあれー。 &lt;p&gt;あ、そうそう、結局EFのCodeFirstなのでデータベースを接続文字列ベースにしたいときは、App.ConfigにconnectionStringsを追加してその名前をDbContextの派生クラス(今回ならBlogContext)のctor : base(“名前”)とすればいいです。   &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-8272056250555076204?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/DxwPU7gYpaM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/DxwPU7gYpaM/db-migration-on-ef4-alpha-3.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh4.ggpht.com/-VdPNedqHiRA/TnvtQ9lxLdI/AAAAAAAACD4/zTnUhVqgr7g/s72-c/Ef41A3_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/09/db-migration-on-ef4-alpha-3.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-7706678687458874873</guid><pubDate>Sun, 28 Aug 2011 13:21:00 +0000</pubDate><atom:updated>2011-08-28T22:22:24.588+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">node.js</category><category domain="http://www.blogger.com/atom/ns#">IIS</category><title>IIS7でnode.js</title><description>&lt;a href="http://www.hanselman.com/blog/InstallingAndRunningNodejsApplicationsWithinIISOnWindowsAreYouMad.aspx"&gt;Installing and Running node.js applications within IIS on Windows - Are you mad? - Scott Hanselman&lt;/a&gt; &lt;p&gt;node.jsをIISで動かす！ファイル更新で自動リサイクルは楽チンでいいですね！受け口がIISだからIISのその他の便利モジュールがそのまま使えるのもいい。&lt;/p&gt; &lt;p&gt;install.batでやってることも紹介してくれてるので、同じ手順でIIS Expressでも大丈夫！なはず。&lt;/p&gt; &lt;p&gt;iisnode.dllっていうネイティブモジュールが橋渡し。よさそうな雰囲気。プロセス数の設定とか、いろいろ調整できるのも素晴らしす。&lt;/p&gt; &lt;p&gt;と、いうわけで、試しに動かしてみました。&lt;/p&gt; &lt;p&gt;インストール手順。&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;a href="http://nodejs.org/#download"&gt;node.js&lt;/a&gt;のバイナリダウンロードしてc:\nodeにコピー&lt;/li&gt; &lt;li&gt;&lt;a href="https://github.com/tjanczuk/iisnode/archives/master"&gt;iisnode&lt;/a&gt;のバイナリをc:\inetpub/iisnodeにコピー&lt;br&gt;※ソースから自分でビルドしても可&lt;/li&gt; &lt;li&gt;iisnode内のinstall.batを実行&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;以上。簡単ですね！node.exeのパスを変えたい場合は、install.batの確認場所の変更と、web.configでnodeProcessCommandLine指定でどーぞー。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-h2rKH9iMMDc/TlpA1q3R4wI/AAAAAAAACCo/WsaVWRTcJbc/s1600-h/node2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="node2" border="0" alt="node2" src="http://lh3.ggpht.com/-Cs8HKnOU0o4/TlpA2BE8WeI/AAAAAAAACCs/AobMhOsksVY/node2_thumb.png?imgmax=800" width="240" height="191"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-Ed2zceDnbsU/TlpA2u3sD2I/AAAAAAAACCw/nWiUzoQmxTs/s1600-h/node%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="node" border="0" alt="node" src="http://lh5.ggpht.com/-AicxfHHSx_E/TlpA3N6e3WI/AAAAAAAACC0/UKe691Jb3T0/node_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;なんかいいね。6000req/sec。たいしたもんだ。ただ、iisnode.dllがちょいちょい落ちる(アプリケーションプールごと道連れ)。まだまだ発展途上。でもネイティブモジュールのスゴさを垣間見れるし、手軽にnode.jsをWindows環境で走らせることが出来ていいです！&lt;/p&gt; &lt;p&gt;ちなみにハンセルマンさんのエントリでも書いてる通り、AppPoolのアカウントをLocalSystemにしとかないと動きません。あと、wcatのsettings.ubrはちゃんとsettings{}で囲んでおきましょう。wcatについてはよく知りませんが(すません)、wcclientが動いてくれなくて、ちゃんと走りませんでした(手動起動で動かした)。&lt;/p&gt; &lt;p&gt;こんなに簡単にできるなら、何か面白そうなことを考えて試してみたり、node.jsでできることいろいろ追いかけて見たくなりますね！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-7706678687458874873?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/R2y9MWNteio" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/R2y9MWNteio/iis7nodejs.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-Cs8HKnOU0o4/TlpA2BE8WeI/AAAAAAAACCs/AobMhOsksVY/s72-c/node2_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/08/iis7nodejs.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4102180175374876011</guid><pubDate>Fri, 19 Aug 2011 09:08:00 +0000</pubDate><atom:updated>2011-08-19T18:18:29.659+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">開発</category><title>HighPerformanceSessionStateProvider</title><description>&lt;p&gt;&lt;a href="http://buchizo.wordpress.com/2011/08/16/asp-net-univarsal-providers-%e3%81%ae%e3%82%bb%e3%83%83%e3%82%b7%e3%83%a7%e3%83%b3%e3%83%97%e3%83%ad%e3%83%90%e3%82%a4%e3%83%80%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e3%81%bf%e3%82%8b-2/"&gt;ASP.NET Univarsal Providers のセッションプロバイダを使ってみる (2) « ブチザッキ&lt;/a&gt;&lt;/p&gt; &lt;p&gt;カメさんのこのエントリーに対して&lt;a href="http://takepara.tumblr.com/post/8996476253/asp-net-univarsal-providers-2"&gt;こんなコメント&lt;/a&gt;をしてみたわけですが、具体的なコードを提示しないで案だけ出すって、そんな失礼なことがあっちゃいけねー。江戸っ子なら宵越しの銭はもってちゃいけねー。江戸っ子じゃないんですけどね。&lt;/p&gt; &lt;p&gt;まずは現状確認。カメさんのコードをいただきつつ。ASP.NET MVC3のサイトをデフォルトで作成。ASP.NET Universal ProvidersをNuGetでインストール。web.configでSessionStateの設定をCookieless="UseUri"とTimeout="1"に変更(検証が簡単だからね)。&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System;
using System.Net;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

      GetPage();

      int count = int.Parse(args[0]);

      for (int i = 0; i &amp;lt; count; i++)
      {
        sw.Reset();
        sw.Start();
        var success = GetPage();
        sw.Stop();
        Console.ForegroundColor = success ? ConsoleColor.White : ConsoleColor.Red;
        Console.WriteLine("{0}\t{1}\t{2}\t{3}", 
            DateTime.UtcNow.ToString("yyyy/MM/dd hh:mm:ss.fff"), 
            sw.Elapsed, i, success ? "OK" : "NG");
      }
    }

    static private bool GetPage()
    {
      var result = true;
      try
      {
        var request = WebRequest.Create("http://localhost:52855/");
        {
          using (var response = request.GetResponse())
          {

          }
        }
      }
      catch
      {
        result = false;
      }

      return result;
    }
  }
}
&lt;/pre&gt;
&lt;p&gt;ずるくないもん！オマージュだもん！&lt;/p&gt;
&lt;p&gt;コレとMVCサイトで実行した結果(クリックでズーム)。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-xFZIUbPMdHo/Tk4n_cn1a0I/AAAAAAAACCQ/J6klxNW1BHE/s1600-h/up1%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="up1" border="0" alt="up1" src="http://lh5.ggpht.com/-4dHkMZcDw_o/Tk4oARFB4tI/AAAAAAAACCU/u5nhPNXgGkc/up1_thumb%25255B1%25255D.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;セッションを生成するコンソールは3個でCPUはMAX。白行はエラーなく進んだところで、赤行はASP.NET側でエラーになったところ。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-ve6qJewWrac/Tk4oA8FOkXI/AAAAAAAACCY/Q1YSNl-zCVw/s1600-h/up2%25255B5%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="up2" border="0" alt="up2" src="http://lh6.ggpht.com/-QTSmJqIbDIc/Tk4oBhflLkI/AAAAAAAACCc/42o23Amy7xY/up2_thumb%25255B1%25255D.png?imgmax=800" width="240" height="138"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;1分でセッションは切れるので、1分後からドカドカ古いセッションの削除が始まります。&lt;/p&gt;
&lt;p&gt;概ね680セッションあたりで飽和。エラーと正常実行を繰り返す感じです。切ないですね。セッションの削除が同時実行されててんやわんやな例外。&lt;/p&gt;
&lt;p&gt;これを解消するために、InitializeRequestをoverrideしたSessionStateProviderクラスを定義します。&lt;/p&gt;
&lt;p&gt;やることはSessionsテーブルにExpires列のインデックスがなければ作成と、セッション初期化時の有効期限切れセッションの削除をEFのDeleteObjectじゃなくSQLのDelete文を実行するように。&lt;/p&gt;
&lt;p&gt;ちょっと長いですけど。&lt;/p&gt;&lt;pre class="prettyprint"&gt;using System;
using System.Configuration;
using System.Data.Common;
using System.Reflection;
using System.Web;
using System.Web.Providers;
using System.Web.Providers.Entities;

namespace UniversalProviders
{
  public class HighPerformanceSessionStateProvider : DefaultSessionStateProvider
  {
    private string _connectionStringName;
    private bool _initialized = false;

    public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
    {
      _connectionStringName = config["connectionStringName"];

      base.Initialize(name, config);
    }

    private void ExecuteSql(ConnectionStringSettings connectionStringSettings, Action&amp;lt;DbProviderFactory, DbCommand&amp;gt; functor)
    {
      var providerName = connectionStringSettings.ProviderName;
      var factory = DbProviderFactories.GetFactory(providerName);
      using (var connection = factory.CreateConnection())
      {
        connection.ConnectionString = connectionStringSettings.ConnectionString;
        connection.Open();

        var command = connection.CreateCommand();

        functor(factory, command);
        command.ExecuteNonQuery();
        connection.Close();
      }
    }

    private void CreateSessionIndex(ConnectionStringSettings connectionStringSettings)
    {
      ExecuteSql(connectionStringSettings, (factory, command) =&amp;gt;
      {
        command.CommandText = @"IF not EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[Sessions]') AND name = N'IX_Sessions')
begin
CREATE NONCLUSTERED INDEX [IX_Sessions] ON [dbo].[Sessions] 
(
[Expires] ASC
) ON [PRIMARY]
end
";
      });
    }

    private void RemoveExpiredSessions(ConnectionStringSettings connectionStringSettings)
    {
      ExecuteSql(connectionStringSettings, (factory, command) =&amp;gt;
      {
        command.CommandText = "delete Sessions where Expires &amp;lt; @0";
        var parameter = factory.CreateParameter();
        parameter.ParameterName = "@0";
        parameter.Value = DateTime.UtcNow;
        command.Parameters.Add(parameter);
      });
    }

    private MethodInfo GetCreateSessionEntities()
    {
      var modelHelper =
        Assembly.GetAssembly(typeof(Session))
                .GetType("System.Web.Providers.Entities.ModelHelper");
      return modelHelper.GetMethod("CreateSessionEntities", 
        BindingFlags.NonPublic | BindingFlags.Static);
    }

    public override void InitializeRequest(HttpContext context)
    {
      var connectionStringSettings = ConfigurationManager.ConnectionStrings[_connectionStringName];
      if (!_initialized)
      {
        var initializer = GetCreateSessionEntities();
        initializer.Invoke(null, new object[] { connectionStringSettings });
        CreateSessionIndex(connectionStringSettings);
        _initialized = true;
      }

      RemoveExpiredSessions(connectionStringSettings);
    }
  }
}&lt;/pre&gt;
&lt;p&gt;クラス名はもちろん&lt;strong&gt;&lt;font size="4"&gt;HighPerformanceSessionStateProvider&lt;/font&gt;&lt;/strong&gt;デス！&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-lQe8m-8h8DY/Tk4oCVEM7-I/AAAAAAAACCg/lA8qv5mEick/s1600-h/up3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="up3" border="0" alt="up3" src="http://lh3.ggpht.com/-RMXVOIN1uSk/Tk4oDLr8BwI/AAAAAAAACCk/tYQjDrRKUY8/up3_thumb.png?imgmax=800" width="240" height="150"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;sessionState mode="Custom" customProvider="HighPerformanceSessionProvider" cookieless="UseUri" timeout="1"&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;providers&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name="DefaultSessionProvider" &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStringName="DefaultConnection" applicationName="/" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;add name="HighPerformanceSessionProvider"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type="UniversalProviders.HighPerformanceSessionStateProvider, UniversalProviders"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connectionStringName="DefaultConnection" applicationName="/" /&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/providers&amp;gt;&lt;br&gt;&amp;lt;/sessionState&amp;gt;&lt;/p&gt;
&lt;p&gt;Web.configも上記のように変更しちゃいましょう！customProviderの指定がDefaultSessionProviderでオリジナルに戻ります。&lt;/p&gt;
&lt;p&gt;セッションが飽和するのが、940件くらいのところです。もちろん例外なんて起きませんよ。有効期限切れのレコード削除なんて何回実行したって、無いものはない！潔し！ちなみに先程の数値もそうですけど、コレが同時実行の限界値なわけではないです。マイノートPCの同時実行の限界くらいです。Webの受付のね。DbProviderFactoriesを利用することでEFと同じだけのポータビリティ(言い過ぎ)。&lt;/p&gt;
&lt;p&gt;名前ほど早くないけど、エラーが起きないから実用的なんじゃん？読み込み性能は気にしてないでーす。あくまで新規セッション作成時のパフォーマンス向上委員会です。言い訳がましい．．．。&lt;/p&gt;
&lt;p&gt;ご利用は計画的に！&lt;/p&gt;
&lt;p&gt;Azureでは動かしてないので、そちらの検証はたぶんカメさんがその３でやってくれるんじゃないでしょーか。&lt;/p&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="https://skydrive.live.com/embedicon.aspx/.Public/SampleProjects/HighPerformanceSessionStateProvider.zip?cid=df0c51db6ad31f29&amp;amp;sc=documents" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4102180175374876011?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/yQn7YuXaokc" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/yQn7YuXaokc/highperformancesessionstateprovider.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-4dHkMZcDw_o/Tk4oARFB4tI/AAAAAAAACCU/u5nhPNXgGkc/s72-c/up1_thumb%25255B1%25255D.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/08/highperformancesessionstateprovider.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4664492054642892782</guid><pubDate>Sun, 14 Aug 2011 11:31:00 +0000</pubDate><atom:updated>2011-08-14T20:31:08.988+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>Controllerを名前から生成するしHTMLを書き換えたりもしてみる</title><description>&lt;p&gt;&lt;a href="http://devadjust.exblog.jp/14342854/"&gt;Developer @ ADJUST : ASP.NET MVC のコントローラクラスを、コントローラ名から取得したいのだけれど...結局、ぜんぶ列挙!?&lt;/a&gt;&lt;/p&gt; &lt;p&gt;前回のエントリーからはや１ヶ月以上経過したこのブログ。エアコンが壊れてて、バリバリ節電に貢献できてる気はするけど、辛い毎日でブログどころではないですよ！&lt;/p&gt; &lt;p&gt;で、坂本さん、上記の件ですが以下のような方法はいかがでしょうか。単純に型が欲しいだけならインスタンスは不要なので無駄な感じはしなくもないですが。必要なのがインスタンスであれば一番いい方法だと思います。&lt;/p&gt;&lt;pre class="prettyprint"&gt;var factory = ControllerBuilder.Current.GetControllerFactory();
var controller = factory.CreateController(Request.RequestContext, "Home");
var type = controller.GetType();
factory.ReleaseController(controller);
&lt;/pre&gt;
&lt;p&gt;コレと言って、自信で発明したものはなく、やってることはMvcHandlerでやってることと同じです。MvcHandlerってあれです、MvcRouteHandlerが返してくるIHttpHandlerのことです。&lt;/p&gt;
&lt;p&gt;内部でタイプキャッシュとかしてくれてるので、パフォーマンスには自信があります！あると思います！&lt;/p&gt;
&lt;p&gt;ここから別件。&lt;/p&gt;
&lt;p&gt;もう暑さで脳みそが働かない．．．。けど、これだけじゃ坂本さんも納得しないと思うので、全然関係ない面白い機能紹介。といっても、URLRewrite 2.0の機能です。ハイパフォーマンスWebサイトの構築には必須のOutputCacheなんですが、クローラーなんかはUser AgentがCookieに対応してないと言ってくるじゃないですか。browserファイルで対応するのが王道ですが、それだと追従していくのが大変面倒くさい。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://learn.iis.net/page.aspx/657/creating-outbound-rules-for-url-rewrite-module/"&gt;Creating Outbound Rules for URL Rewrite Module : URL Rewrite Module 2 : URL Rewrite Module : The Official Microsoft IIS Site&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;想定している状況を簡単にいうと、SessionのcookielessはuseDeviceProfile。PCサイトとケータイサイトを同一アプリケーションで実行。PCサイトは積極的にOutputCacheを利用し、ケータイサイトでは消極的(Viewでは使わない)に利用。この場合、PCサイトにクローラが来るとそのままではCookielessとしてセッションが生成されるので、OutputCacheにSessionIDを保持したHTMLが出力されてしまって都合悪い！&lt;/p&gt;
&lt;p&gt;ということなんですが、伝わるでしょうか．．．。&lt;/p&gt;
&lt;p&gt;OutputCacheをVaryByUserAgentにするのも手ですね。いろいろ方法はあると思うんですけど、こないだ試したのがPCサイトの場合、出力されるHTMLにSession IDが含まれることが正常系ではありえないので、PCサイトの場合、Response.FilterでURLにSession IDが含まれてたら消してしまうという方法。Response.Filterでやるよりももっと簡単なのがURL Rewrite 2.0の出力書き換えを利用するっていう方法。どうですか、面白そうじゃないですか？&lt;/p&gt;
&lt;p&gt;ちなみにOutbound Rule用に独自Provider書いたりもできるけど、そこまでやることはあんまりないかなー、と思いますがどうでしょう。&lt;/p&gt;&lt;pre class="prettyprint"&gt;&amp;lt;rewrite&amp;gt;
  &amp;lt;outboundRules&amp;gt;
      &amp;lt;rule name="Sessionless" preCondition="html" enabled="true"&amp;gt;
          &amp;lt;match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script, CustomTags" customTags="All" pattern="(.*)/\(S\([0-9a-z]+\)\)(.*)" /&amp;gt;
          &amp;lt;action type="Rewrite" value="{R:1}{R:2}" /&amp;gt;
          &amp;lt;conditions&amp;gt;
              &amp;lt;add input="{REQUEST_URI}" matchType="Pattern" pattern="\)/mobile" ignoreCase="true" negate="true" /&amp;gt;
          &amp;lt;/conditions&amp;gt;
      &amp;lt;/rule&amp;gt;
      &amp;lt;customTags&amp;gt;
          &amp;lt;tags name="All" /&amp;gt;
      &amp;lt;/customTags&amp;gt;
      &amp;lt;preConditions&amp;gt;
          &amp;lt;preCondition name="html"&amp;gt;
              &amp;lt;add input="{RESPONSE_CONTENT_TYPE}" pattern="text/html" /&amp;gt;
              &amp;lt;add input="{REQUEST_URI}" pattern="/mobile" negate="true" /&amp;gt;
          &amp;lt;/preCondition&amp;gt;
      &amp;lt;/preConditions&amp;gt;
  &amp;lt;/outboundRules&amp;gt;
&amp;lt;/rewrite&amp;gt;
&amp;lt;urlCompression doDynamicCompression="false" /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;だいたいこんな感じの設定です。これをWeb.configに書いておきましょう。system.webServer配下です。ケータイサイトが/mobile配下という前提です。&lt;/p&gt;
&lt;p&gt;実験。MVCサイトをデフォルトのまま生成し、Web.configに上記設定を追加しないで動作を確認。&lt;/p&gt;
&lt;p&gt;この状態で出力されるのは↓。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-gEnN1pf7xqE/Tkex7FJ8xPI/AAAAAAAACBw/oauLJzDlww8/s1600-h/cookieless1%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cookieless1" border="0" alt="cookieless1" src="http://lh6.ggpht.com/-Kl1bq3gQ1yw/Tkex7gcgj1I/AAAAAAAACB0/wYhIasZlH-w/cookieless1_thumb.png?imgmax=800" width="240" height="191"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;そりゃそうですね。中身は↓。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-a95CBFDEjjs/Tkex8U5okeI/AAAAAAAACB4/vNSZehuiLMs/s1600-h/cookieless2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cookieless2" border="0" alt="cookieless2" src="http://lh4.ggpht.com/-FPyBledJqfA/Tkex8wHhWZI/AAAAAAAACB8/LFjFxMtKdzA/cookieless2_thumb.png?imgmax=800" width="240" height="191"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なんてことないですよね。&lt;/p&gt;
&lt;p&gt;今度はsessionStateのcookielessをUseUriにしてみましょう。見た目は同じなのでソースだけ。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-kYM4YOzgAhQ/Tkex9SUrEvI/AAAAAAAACCA/TVVNS1G8h9I/s1600-h/cookieless3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="cookieless3" border="0" alt="cookieless3" src="http://lh3.ggpht.com/-Ufe-5o1czBI/Tkex96QY-vI/AAAAAAAACCE/gVc5U7bQvDE/cookieless3_thumb.png?imgmax=800" width="240" height="191"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;画像をクリックして大きくしてみるとわかりますが、Session IDを含んだURLですよね。&lt;/p&gt;&lt;pre class="prettyprint"&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;title&amp;gt;Home Page&amp;lt;/title&amp;gt;
    &amp;lt;link href="/(S(f4grns0hiatkwquk55qulbmd))/Content/Site.css" rel="stylesheet" type="text/css" /&amp;gt;
    &amp;lt;script src="/(S(f4grns0hiatkwquk55qulbmd))/Scripts/jquery-1.5.1.min.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="/(S(f4grns0hiatkwquk55qulbmd))/Scripts/modernizr-1.7.min.js" type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class="page"&amp;gt;
        &amp;lt;header&amp;gt;
            &amp;lt;div id="title"&amp;gt;
                &amp;lt;h1&amp;gt;My MVC Application&amp;lt;/h1&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div id="logindisplay"&amp;gt;
                    [ &amp;lt;a href="/(S(f4grns0hiatkwquk55qulbmd))/Account/LogOn"&amp;gt;Log On&amp;lt;/a&amp;gt; ]
            &amp;lt;/div&amp;gt;
...以下省略
&lt;/pre&gt;
&lt;p&gt;今度は先のURLRewrite設定をOnにした場合。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-f8bMKIs1KY4/Tkex-alj8iI/AAAAAAAACCI/T0fjTtsCbho/s1600-h/cookieless4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="cookieless4" border="0" alt="cookieless4" src="http://lh5.ggpht.com/-HV-AwGfpc1k/Tkex_BBhFTI/AAAAAAAACCM/inrWXx5bJ4Y/cookieless4_thumb.png?imgmax=800" width="240" height="191"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;あら素敵。ウソじゃないよ！URLにはちゃんとSession ID入ってるでしょ？&lt;/p&gt;
&lt;p&gt;これでクローラが来てもOutputCacheにへんてこなものが仕込まれることないですね！あと、動的圧縮をオフにしてるのは既知です。設定いいんですけど今回はこれで。&lt;/p&gt;
&lt;p&gt;役に立つやら立たないやらな情報でした！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4664492054642892782?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/CYHuumpDWvQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/CYHuumpDWvQ/controllerhtml.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/-Kl1bq3gQ1yw/Tkex7gcgj1I/AAAAAAAACB0/wYhIasZlH-w/s72-c/cookieless1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/08/controllerhtml.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-968979115532186264</guid><pubDate>Thu, 30 Jun 2011 03:45:00 +0000</pubDate><atom:updated>2011-06-30T12:45:55.649+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>全角数値を半角数値に変換するModelBinder</title><description>&lt;blockquote&gt; &lt;p&gt;拝啓、まゆきっつぁん&lt;/p&gt; &lt;p&gt;いつも、&lt;a href="http://www.theshodo.com/"&gt;The Shodo&lt;/a&gt;にて筆の練習をさせていただいてます。ただ、いつまで経っても筆ぺんでは上手にかけるようになれません。本物の習字道具を使わないと練習の成果が出ないのでしょうか？The Fudepenで練習すると効果が期待できるかもしれないですね。是非、考慮いただければと思います。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;先日のmvcConf @:Japan懇親会での一件について。ふと、思い出したので書いてみました。あの時はValueProviderがどうのこうのという話になったような気がしないでもないですが、ValueProviderではレイヤ低すぎて型は意識されてないのダメですね。&lt;/p&gt; &lt;p&gt;こんな感じでいかがでしょうか？整数に限定してますが、応用すると他にもいろいろできると思います。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public class IntegralModelBinder : DefaultModelBinder
{
  private readonly List&amp;lt;Type&amp;gt; _integralTypes = new List&amp;lt;Type&amp;gt;
  {
    typeof (sbyte),
    typeof (byte),
    typeof (char),
    typeof (short),
    typeof (ushort),
    typeof (int),
    typeof (uint),
    typeof (long),
    typeof (ulong)
  };
  private const string WideIntegrals = "１２３４５６７８９０一二三四五六七八九零壱弐参肆伍陸柒捌玖零";
  private const string NarrowIntegrals = "123456789012345678901234567890";

  protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
  {
    if (!_integralTypes.Contains(propertyDescriptor.PropertyType) || value != null)
    {
      base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
      return;
    }

    var providerResult = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
    value = providerResult.AttemptedValue;

    var narrow = string.Join("",(value + "").Select(c =&amp;gt; WideIntegrals.Contains(c) ? NarrowIntegrals[WideIntegrals.IndexOf(c)] : c));
    var converter = TypeDescriptor.GetConverter(propertyDescriptor.PropertyType);
    try
    {
      value = converter.ConvertFrom(narrow);
      bindingContext.ModelState.Remove(propertyDescriptor.Name);
    }
    catch{}

    base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
  }
}&lt;/pre&gt;
&lt;p&gt;試しに以下のようなモデルを定義してみました。&lt;/p&gt;&lt;pre class="prettyprint"&gt;    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public DateTime? Birthday { get; set; }
        public byte Rank { get; set; }
    }&lt;/pre&gt;
&lt;p&gt;AgeとRankが整数型なので処理対象となります。Global.asaxでDefaultBinder(ModelBinders.Binders.DefaultBinder = new IntegralModelBinder();)を差し替えて実行した結果は↓こんな感じになります。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-mp-RUMjI5pE/TgvxYrmGonI/AAAAAAAACAE/_deFQ11GnOk/s1600-h/mb1%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mb1" border="0" alt="mb1" src="http://lh3.ggpht.com/-33dAeXWq0w8/TgvxZB5fbXI/AAAAAAAACAI/Xr1mTcXy-KY/mb1_thumb.png?imgmax=800" width="240" height="225"&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class="prettyprint"&gt;@model ZenBinder.Models.Person
@{
    ViewBag.Title = "ホーム ページ";
}

&amp;lt;h2&amp;gt;@ViewBag.Message&amp;lt;/h2&amp;gt;

@using (Html.BeginForm())
{
    @Html.EditorForModel()

    &amp;lt;button type="submit"&amp;gt;送信&amp;lt;/button&amp;gt;
}&lt;/pre&gt;
&lt;p&gt;まずはフォームを出すでしょう。簡単にEditorForModelを使います。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-kTMXbcJR5U8/TgvxZnrCGOI/AAAAAAAACAM/33q-KMP22GM/s1600-h/mb2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mb2" border="0" alt="mb2" src="http://lh5.ggpht.com/-XEu5lVHQsb8/TgvxaCSpLtI/AAAAAAAACAQ/5Sy8Ya0e9I4/mb2_thumb.png?imgmax=800" width="240" height="225"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;普通に半角だけで試して送信してみると、ちゃんと動きます。同じものを全角にしてみても結果は同じになります。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/-pfxi6ID_vlI/TgvxanLyF4I/AAAAAAAACAU/Y249XncTT_o/s1600-h/mb3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mb3" border="0" alt="mb3" src="http://lh5.ggpht.com/-cw5-7P66Cqk/Tgvxa6Y6XtI/AAAAAAAACAY/33BNSYu5reo/mb3_thumb.png?imgmax=800" width="240" height="225"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;変換出来ない場合はDefaultModelBinderの挙動になります。十とか百とか千も変換するためのマッピングを用意すれば、もう少しオシャレさが増すかもしれないです。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-t4h38r-0I8s/Tgvxb8UONrI/AAAAAAAACAc/tdqHQphozdA/s1600-h/mb4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mb4" border="0" alt="mb4" src="http://lh3.ggpht.com/-nGOmqUnDV6Y/TgvxclDtSLI/AAAAAAAACAg/G7QtXnPNEhQ/mb4_thumb.png?imgmax=800" width="240" height="225"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;いかがでしょう。ケータイでの入力に是非応用してみてください。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-968979115532186264?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/SZF8qRuA5EQ" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/SZF8qRuA5EQ/modelbinder.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/-33dAeXWq0w8/TgvxZB5fbXI/AAAAAAAACAI/Xr1mTcXy-KY/s72-c/mb1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/06/modelbinder.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-7274089194884136072</guid><pubDate>Fri, 17 Jun 2011 15:02:00 +0000</pubDate><atom:updated>2011-06-18T11:00:40.778+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">mvcConf</category><title>mvcPhotosの出来るまで</title><description>&lt;p&gt;ちょっと書いてみます。あんまり面白く無いですよ。&lt;/p&gt; &lt;p&gt;ASP.NET MVCをメインにした企画をやってみたいとチャックから連絡があって、喋る人が決まったところで各々にテーマがふられました。んで、たけはら担当として「クライアントサイドのテクノロジをメインに扱うセッションを」というリクエストから始まるんですが、ぶっちゃけ「それMVC関係ないじゃない．．．」と愕然としたものです。&lt;/p&gt; &lt;p&gt;とはいえ、テーマを無視するのもあれだからと、まずはセッション概要を伝え(もちろんこの時にはまだ何をするのか決めてないので、ぼかしまくった感じで)募集開始。そろそろ真面目に考えないとね～、と思いつつ仕事も忙しかったしで、ほっといたらあっという間に5月中旬。そろそろスライドだけでも書かねばと書き始めるものの、何を作るかは全く決まらず．．．。とりあえずの方向性としては&lt;/p&gt; &lt;ol&gt; &lt;li&gt;作ったものに参加者もその場でアクセスできる(だけど会場内のネットワークでは自マシンに参加者がアクセス出来ないはず)  &lt;li&gt;アーキテクチャを意識する  &lt;li&gt;MVC使いつつクライアントサイドモリモリ  &lt;li&gt;コードは参加者へのプレゼント&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;の４点。&lt;/p&gt; &lt;p&gt;※ネットワークについては、昨年のTechEDでダメだったのを経験してたので。&lt;/p&gt; &lt;p&gt;はてさて、どうしたものか。Twitterは他の人が絶対利用するだろうからパス。単純に外部サーバーにWebアクセスしてみんなでワイワイする感じで何がいいかな～、って考えるとどうしてもMvcGraffiti(みんなでお絵かき)とかぶる。となると、手堅い方法はメールか。ケータイなら絶対繋がるはず。メールということはPOP3の実装は必要だな～。メールなら写メがいいかも。という流れでとりあえずPOP3の実装と画像のリサイズ部分だけ先行コーディング。&lt;/p&gt; &lt;p&gt;いろいろ悩んだ挙句、普通に写真共有っていうところに落ち着くんだけど、どうやってクライアントサイドモリモリを達成するか。そこは後回しにしてアーキテクチャ。この時点でAppHarborをプラットフォームにして、ストレージはGoogle Storage使ってみようかと実験。でもGoogle Storageが思ったようにいかないからS3にチェンジ。モデルはCodeFirstで、ストレージは置き換えられるようにProviderとして実装。MVCでのサーバーサイドはシンプルにAPI的なものと、UA切り替え出来るような仕組みをどうするか考えつつ、ワーカーによるバックグラウンド処理でメールとストレージのつなぎをやろうと決める。UAに合わせてViewを切り替えるのはいろんなやり方があるけど、出回ってるやり方を実装してもつまらないので、随分悩んだ末にRoutingとViewEngineのコンビネーションで行う方法を思いつく。さすがオレ。&lt;/p&gt; &lt;p&gt;クライアントサイドはこのころすごく気になってたknockout.jsとModernizrを使うことで、うまいことやろうと思いつつ具体的なことは決めずにサーバーサイドをゴリゴリ作る。あと、それっぽくテストコードも用意することで、スタックというかレイヤというか、その辺を意識しやすいようにしておこう。&lt;/p&gt; &lt;p&gt;ちょっと横道にそれますが、ControllerのテストをしやすくするためにFormCollectionをパラメータに使う例をよく見ますね。でも意味ないですよね。そこは普通に入力モデルを渡せばいいじゃないっていうふうに思うわけですよ。だって、FormなりのValueProviderからModelBinder経由した入力モデルへのマッピングって、Controllerのテストじゃ無いからですよ。通常のFormを想定した場合、FormCollectionからUpdateModelをするなら、それはもうMVCが提供してくれるModelBinderのテストをするようなもんでしょう。意味無いじゃん！なので、ModelBinderのテストはMVCの開発チームに任せて、自分たちの書いたコードに対するテストを書きましょう。ただ、今回Controllerのテストは書いてないですけどね！てへ。&lt;/p&gt; &lt;p&gt;ここまで全然コード解説じゃないですけど、セッションで話したいのはそもそも製品の紹介じゃなくて「アプリケーションの作り方」。何をどういう設計で作るかを決めることで詰みです。「拳(コード)で語る」のがプログラマーですよ。だって、ASP.NET MVCも１～３へと進み、４以降になったら製品の使い方、コードの書き方なんて変わるわけですよ。Razorなんてものも出てくるし。だけど、考え方とか適用の仕方ってMVCっていうアーキテクチャスタイルとか、デザインパターンとかって基礎として使い続けるじゃん？そこ意識すること大事だと思うんす。&lt;/p&gt; &lt;p&gt;ただでも、このやり方は諸刃で、聞きに来てくれる層によってはドン引きされるんです(経験あり)。だって「お前の作ったアプリケーションを見に来たんじゃないんだよ！」とか「そのアプリでオレのプロジェクトは解決できない！」という意見もあるからね。いろいろです。どんな意見も、それぞれの人のコンテキストでは正論っす。ただ、自分にとっては、目の前の問題の答えを提示する局所最適じゃなく、全体最適を目指すほうが楽しい。なので、これからもこのやり方は変わらないでしょう。&lt;/p&gt; &lt;p&gt;はっ！熱くなってた！しっけいしっけい。&lt;/p&gt; &lt;p&gt;サーバーサイドが概ね出来たところで、クライアントサイドの実装に入るわけですが、PC,iPad,iPhone、そして日本が誇る超精密パーソナル通信機器、通称ガラケー対応も無視できない。モダンブラウザ向けにはjQuery Mobile使おうと思ってたんだけど、Azure担当大臣のだいちゃんが「jQuery Mobileでモテモテになるっす！」とか言い出しやがって、かぶるじゃねーかよ、的なね。まぁ、いいか。んじゃ、オレ適当に実装する、ということになりました。なので、見た目かなりしょぼくなったけど、オレのせいじゃないから！エロ大臣のせいだから！！Azureチーム金持ってるからって可愛いキャラ多すぎなんだよ。ASP.NET界隈では緑のキグルミしかいないっつーの。羨ましくなんか．．うっぐ。泣かない。&lt;/p&gt; &lt;p&gt;はいはい。クライアントサイドでどういう感じに動かすのか、図に書いて最初に実装したのが、↓こんなやつ。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-yhCIn8ukiKk/Tftshpw5UWI/AAAAAAAAB_I/SaovWJyL8n8/s1600-h/sample%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="sample" border="0" alt="sample" src="http://lh5.ggpht.com/-nmbZ06UNoyM/TftsifrcrEI/AAAAAAAAB_M/Lbwxn_BQrw4/sample_thumb.png?imgmax=800" width="240" height="171"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;受信画像をタイル状にランダム表示。緑も意識してみた。だけどコレがまたダサいのなんのって。イメージ通りに作ったはずなのに。自分のセンスに絶望。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/--dMYO5ruVS8/TftsjGH6CxI/AAAAAAAAB_Q/z48neGeev_E/s1600-h/sample2%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="sample2" border="0" alt="sample2" src="http://lh4.ggpht.com/-Qjg3GwFkyEc/TftsjgUSFcI/AAAAAAAAB_U/SGibZ9_Oeio/sample2_thumb.png?imgmax=800" width="240" height="159"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ランダムがだめなのかと思って順番に表示するようにしたりしたけど．．．。根本的におかしい。マジやべー。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-LjhIENIscPo/TftskiR2U_I/AAAAAAAAB_Y/JQrBCoZlTpY/s1600-h/sample3%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="sample3" border="0" alt="sample3" src="http://lh3.ggpht.com/-zP6ewV_e7Rg/TftslAmTKSI/AAAAAAAAB_c/kWdp0DXfJOw/sample3_thumb.png?imgmax=800" width="240" height="159"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;色がだめなのか！？と思って黒くしてみた。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-CtFBeSoucyM/TftsmP2YEbI/AAAAAAAAB_g/v_h9jND0y84/s1600-h/sample4%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="sample4" border="0" alt="sample4" src="http://lh4.ggpht.com/-kINeiovFh2w/TftsmwDlyeI/AAAAAAAAB_k/X3j_eXcOBH0/sample4_thumb.png?imgmax=800" width="240" height="159"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;で、最終的には↑こうなるんだけど、みんな知ってた？ウィンドウサイズに合わせてサムネイルの画像サイズは１００→５０→２５と収まりよくなるようにリサイズするんだよ？&lt;/p&gt; &lt;p&gt;&lt;a href="http://mvcphotos.takepara.com/"&gt;http://mvcphotos.takepara.com/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;試してみてね。&lt;/p&gt; &lt;p&gt;ソースはこちら。&lt;/p&gt; &lt;p&gt;&lt;a href="http://mvcphotos.codeplex.com/"&gt;http://mvcphotos.codeplex.com/&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Source Codeタブをクリックして右端のLatest VersionにあるBrowseで見たり、Downloadで取得してね。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/-c_VPoT2V_mM/TfwGxcjA1TI/AAAAAAAAB_o/aKp-fUUVtSg/s1600-h/source%25255B2%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="source" border="0" alt="source" src="http://lh3.ggpht.com/-7eq-yMnBWpY/TfwGxzWrTQI/AAAAAAAAB_s/VIZzeGyW8FU/source_thumb.png?imgmax=800" width="240" height="104"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;書き疲れた．．．。もういい？仕様書もマニュアルもなしで、コードを追いかけるのも大変だと思うので、今回作成したmvcPhotosの実装をザックリ書きだしておきます。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;POP3でメール受信  &lt;li&gt;クラウドストレージとローカルストレージを切り替えやすくするためのストレージプロバイダ化  &lt;li&gt;画像のリサイズ  &lt;li&gt;EF CodeFirstによるDAL  &lt;li&gt;データベースアクセスをRepositoryにより抽象化  &lt;li&gt;サーバーサイドでもDAL用のモデルと、入出力用のモデルを分けることでレイヤ分離と検証ルールの明確化  &lt;li&gt;動的画像リサイズを行うためのコンカレント制御と非同期Controller  &lt;li&gt;複数のUserAgentを同一Controllerで処理するためのViewEngineとRouting制御  &lt;li&gt;モダンブラウザの判定と、動的スクリプト読み込みにModernizr  &lt;li&gt;knockout.jsによるクライアントサイドでのMVVM実装  &lt;li&gt;自作Service Locatorと、DependencyResolver実装  &lt;li&gt;DIを３パターン実装(探してみてね！)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;こんな感じです。&lt;/p&gt; &lt;p&gt;ちなみにパネルディスカッションの最後でゴニョゴニョ言ってたことなんですけど「大事なのはMVCの心を理解しようとし、SoC - Separation of Concerns - 関心の分離を意識すること」。つまりきちんと役割を分離して、実装も可能なかぎり分離して参照関係を単純にしましょうね、と言いたかったですが言葉足らずのドヤ顔で失礼しました。&lt;/p&gt; &lt;p&gt;あと、一色さん、変なやりとりでスマセンした。失礼ぶっこいてスマセンした。ホントはすごいシャイボーイなんです。自分で言うのもなんですが、草食系男子なんです。型は古くて時化には強いタイプですけどシャバいやつなんす。&lt;/p&gt; &lt;p&gt;今後ともご贔屓に～。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-7274089194884136072?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/veBW7t35Nug" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/veBW7t35Nug/mvcphotos.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/-nmbZ06UNoyM/TftsifrcrEI/AAAAAAAAB_M/Lbwxn_BQrw4/s72-c/sample_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/06/mvcphotos.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4668333263834965797</guid><pubDate>Mon, 13 Jun 2011 13:00:00 +0000</pubDate><atom:updated>2011-06-13T22:00:12.433+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><category domain="http://www.blogger.com/atom/ns#">mvcConf</category><title>メモ帳ですいません</title><description>&lt;p&gt;mvcConf @:Japanでしゃべりましたね。あまりにも大雑把な説明っぷりに自分でもビックリです。オレ、こんなに雑だったんだ．．．もっと繊細だと思ってた。なんつっ亭たけはらです。&lt;/p&gt; &lt;p&gt;雨の中、足を運んでくれたたくさんの参加者の方々に感謝の気持ちでいっぱいです。&lt;/p&gt; &lt;p&gt;mvcConfといえば、知る人ぞ知るマニアにはたまらないイベントです。それを日本で日本人だけで、ASP.NETバカ集合させてしゃべらせようと企んだチャックの度量には度肝を抜かれました。初めての試みだし、人が集まるのか、どのような背景の人が参加してくれるのか全く未知数で、ランチも交通費もでないという低予算のなか、よくもまぁなんとかなったもんですね。&lt;/p&gt; &lt;p&gt;個人的には全然言いたいことがいえずに、あっという間に終わってしまったので、ブログを通して言い足りなかった部分、特にサンプルに盛り込んでいるテクノロジや設計方針なんかを、ちょびっとだけ書き残しておこうと思います。&lt;/p&gt; &lt;p&gt;まず当日、スライドの内容が思い出せるきがしなかったので、メモを書きました。それがコレ↓です。&lt;/p&gt; &lt;h3&gt;mvcPhotos&lt;/h3&gt; &lt;p&gt;&amp;nbsp; &lt;a href="http://mvcphotos.takepara.com"&gt;http://mvcphotos.takepara.com&lt;/a&gt;&lt;br&gt;&amp;nbsp; &lt;a href="http://mvcphotos.takepara.com/mobile"&gt;http://mvcphotos.takepara.com/mobile&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp; mvcphotos@takepara.com&lt;/p&gt; &lt;h3&gt;サイトURL設計&lt;/h3&gt; &lt;p&gt;&amp;nbsp; Home&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Index&lt;br&gt;&amp;nbsp; Photos&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Index - Jsonable&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Tags – Jsonable&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Create (GET/POST)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Image&lt;br&gt;&amp;nbsp; Tags&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Index – Jsonable&lt;br&gt;※/mobile配下も同じ&lt;/p&gt; &lt;h3&gt;アーキテクチャとしてみるWeb Stack&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;ViewEngineでのView切り替え&lt;br&gt;同一コントローラを利用する&lt;/li&gt; &lt;li&gt;knockoutjsを使ってMVVMなクライアントサイド実装&lt;/li&gt; &lt;li&gt;Modenizrでのブクライアントサイド機能判定&lt;br&gt;Modernizr.load活用&lt;/li&gt; &lt;li&gt;クラウド利用によるスケール、可用性の確保&lt;br&gt;AppHarborとAWS&lt;/li&gt; &lt;li&gt;メールを利用したデータ入力&lt;/li&gt; &lt;li&gt;サーバーサイドを極力API化&lt;br&gt;ODataを出力しdatajsで取得する。&lt;/li&gt; &lt;li&gt;テスト&lt;br&gt;少しずつでいいからテストも書いていこうね&lt;br&gt;Mockじゃなくても、StubやFakeを用意する。&lt;/li&gt; &lt;li&gt;razordo.it / guttokita.ccもよろしく！&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;参照ページ&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;IS 7.0 における ASP.NET アプリケーションのライフ サイクルの概要&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/library/bb470252.aspx"&gt;http://msdn.microsoft.com/ja-jp/library/bb470252.aspx&lt;/a&gt;&lt;/li&gt; &lt;li&gt;ASP.NET ページのライフ サイクルの概要&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/library/ms178472.aspx"&gt;http://msdn.microsoft.com/ja-jp/library/ms178472.aspx&lt;/a&gt;&lt;/li&gt; &lt;li&gt;ASP.NET 2.0 Provider Model: Introduction to the Provider Model&lt;br&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa479030.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa479030.aspx&lt;/a&gt;&lt;/li&gt; &lt;li&gt;HTML5 Showcase for Web Developers: The Wow and the How&lt;br&gt;&lt;a href="http://www.htmlfivewow.com/"&gt;http://www.htmlfivewow.com/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Architecture Center - MSDN -&lt;br&gt;&lt;a href="http://msdn.microsoft.com/es-es/architecture/en/"&gt;http://msdn.microsoft.com/es-es/architecture/en/&lt;/a&gt;&lt;/li&gt; &lt;li&gt;The art of simplicity: Using the AsyncController in ASP.NET MVC 2&lt;br&gt;&lt;a href="http://bartwullems.blogspot.com/2010/01/using-asynccontroller-in-aspnet-mvc-2.html"&gt;http://bartwullems.blogspot.com/2010/01/using-asynccontroller-in-aspnet-mvc-2.html&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Last Call: W3C Invites Broad Review of HTML5 and Five Related Specifications&lt;br&gt;&lt;a href="http://www.w3.org/News/2011.html#entry-9105"&gt;http://www.w3.org/News/2011.html#entry-9105&lt;/a&gt;&lt;/li&gt; &lt;li&gt;HTML5と関連仕様がラストコール（最終草案）となる。W3Cが幅広いレビューを募集 － Publickey&lt;br&gt;&lt;a href="http://www.publickey1.jp/blog/11/html5w3c_1.html"&gt;http://www.publickey1.jp/blog/11/html5w3c_1.html&lt;/a&gt;&lt;/li&gt; &lt;li&gt;20 Things I Learned About Browsers and the Web&lt;br&gt;&lt;a href="http://www.20thingsilearned.com/ja-JP"&gt;http://www.20thingsilearned.com/ja-JP&lt;/a&gt;&lt;/li&gt; &lt;li&gt;DependencyResolver&lt;br&gt;Brad Wilson: ASP.NET MVC 3 Service Location, Part 1: Introduction&lt;br&gt;&lt;a href="http://bradwilson.typepad.com/blog/2010/07/service-location-pt1-introduction.html"&gt;http://bradwilson.typepad.com/blog/2010/07/service-location-pt1-introduction.html&lt;/a&gt;&lt;/li&gt; &lt;li&gt;Fun with ASP.NET MVC 3 and MEF | MIX11 | Channel 9&lt;br&gt;&lt;a href="http://channel9.msdn.com/events/MIX/MIX11/OPN07"&gt;http://channel9.msdn.com/events/MIX/MIX11/OPN07&lt;/a&gt;&amp;nbsp;&lt;/li&gt; &lt;li&gt;WebFormでの開発はページコントローラパターン&lt;br&gt;ページ コントローラ&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/library/ms978764.aspx"&gt;http://msdn.microsoft.com/ja-jp/library/ms978764.aspx&lt;/a&gt;&lt;/li&gt; &lt;li&gt;ASP.NET でのページ コントローラの実装&lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/library/ms998548.aspx"&gt;http://msdn.microsoft.com/ja-jp/library/ms998548.aspx&lt;/a&gt;&lt;/li&gt; &lt;li&gt;InfoQ: モバイル開発についての調査&lt;br&gt;&lt;a href="http://www.infoq.com/jp/news/2011/05/A-Survey-on-Mobile-Development"&gt;http://www.infoq.com/jp/news/2011/05/A-Survey-on-Mobile-Development&lt;/a&gt;&lt;/li&gt; &lt;li&gt;単体テスト: テスト代替の連続性について検討する &lt;br&gt;&lt;a href="http://msdn.microsoft.com/ja-jp/magazine/cc163358.aspx"&gt;http://msdn.microsoft.com/ja-jp/magazine/cc163358.aspx&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;ココまで。&lt;/p&gt; &lt;p&gt;Jsonableは渾身の仕込みだったけど、響かなくて残念でした。&lt;/p&gt; &lt;p&gt;リンク多いけど、参考になるサイトばかりで紹介しておきたかったのでメモに書いてました。気が向いたらのぞきに行ってみてはどうでしょう。&lt;/p&gt; &lt;p&gt;パネルディスカッションで「Page Controller」がどうのこうの言ってたんですけど、なんとなくPageとControlと聞き間違いされてる気がしたので改めて「Page Controller」デス！&lt;/p&gt; &lt;p&gt;実装サンプルを用意して適用方法を解説しようというセッションなので、最初からセッション資料なんてこれだけでよかったかも。今度からそうしようかな。技術的な解説よりもライブコーディングをいれたほうがピンと来やすいかもしれないしねー。「御託はいいよ・・・拳で語ってくれ」と誰もが思ったことでしょう。&lt;/p&gt; &lt;p&gt;次のエントリはコード解説を書こうと思います。が、途中でギャー！ってなってポイってしても、そこは笑って許してね！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4668333263834965797?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/1IBrOiC6CHU" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/1IBrOiC6CHU/blog-post.html</link><author>noreply@blogger.com (たけはら)</author><feedburner:origLink>http://takepara.blogspot.com/2011/06/blog-post.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-2218932394589394051</guid><pubDate>Sun, 05 Jun 2011 01:08:00 +0000</pubDate><atom:updated>2011-06-05T10:16:54.056+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C#</category><title>ClayFactory</title><description>&lt;p&gt;Clay面白いかも！&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.hanselman.com/blog/NuGetPackageOfTheWeek6DynamicMalleableEnjoyableExpandoObjectsWithClay.aspx?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+ScottHanselman+%28Scott+Hanselman+-+ComputerZen.com%29"&gt;NuGet Package of the Week #6 - Dynamic, Malleable, Enjoyable Expando Objects with Clay - Scott Hanselman&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://clay.codeplex.com/"&gt;Clay - Home&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ORM使ったリポジトリを使った場合、戻りの型って結構融通効きにくいじゃないですか。select対象のクラスを全部手書きするのかよ、って。だからといって無名クラスは戻せないじゃない。objectでリフレクションとか本末転倒。&lt;/p&gt; &lt;p&gt;なのでdynamicを使うことにするんですけど、そのためにLINQ selectの射影をdynamicにする簡単なヘルパーを用意しとくと便利ですね。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public static class ObjectToDynamicExtensions
{
  public static dynamic ToDynamic(this object obj)
  {
    dynamic model = new ExpandoObject();
    var properties = obj.GetType().GetProperties();
    foreach (var property in properties)
    {
      ((IDictionary&amp;lt;string, object&amp;gt;)model)[property.Name] = property.GetValue(obj, null);
    }
    return model;
}
}&lt;/pre&gt;
&lt;p&gt;使用例&lt;/p&gt;&lt;pre class="prettyprint"&gt;return from person in db.People
       select new {
         FullName = person.FirstName + 
                    " " + 
                    person.LastName,
         Age = ((DateTime.Today.Year*10000 + 
               DateTime.Today.Month*100 + 
               DateTime.Today.Day) - 
               (person.Birthday.Year*10000 + 
               person.Birthday.Month*100 + 
               person.Birthday.Day))/10000
       }.ToList().ToDynamic()
       &lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;使い道はこんな感じのマッピングなんですけど、ここでちょっと残念なのが、ASP.NET MVCのViewにdynamicを渡すと、ほとんどのヘルパーが機能してくれないこと。残念ですね。凹みますね。&lt;/p&gt;
&lt;p&gt;ちゃんと型付けしとけ、っていうのはもちろんそうなんですけどね。だからScaffoldingもあるわけでして。&lt;/p&gt;
&lt;p&gt;とは言え、柔軟な実装にしたい。単純にマッピングをしたいようなときにはClayいいかもという流れです。&lt;/p&gt;
&lt;p&gt;dynamicをインターフェース実装のプロキシに変換してくれるみたい。そうなればもう強い型付け完了。&lt;/p&gt;
&lt;p&gt;インターフェースは規約で判定するみたいだけど、namespaceどうするんだろ。気になる。あ、でも、Entity ModelとOutput Modelを両方共クラスにするんじゃなくて、Output Modelはインターフェースにしちゃう感じなのかな。&lt;/p&gt;
&lt;p&gt;dynamicから特定のインターフェースに変換したくなるときって、よくあるきがしてきた！ワクワクするね！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-2218932394589394051?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/LaY889CBchg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/LaY889CBchg/clayfactory.html</link><author>noreply@blogger.com (たけはら)</author><feedburner:origLink>http://takepara.blogspot.com/2011/06/clayfactory.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-7454442405168692471</guid><pubDate>Fri, 06 May 2011 05:30:00 +0000</pubDate><atom:updated>2011-05-06T14:30:39.475+09:00</atom:updated><title>@RenderPageとHtml.RenderPartial</title><description>&lt;p&gt;いや～、連休ももうすぐ終わってしまうと思うと、ちょっぴりアンニュイな気持ちになってしまいます。アンニュイってララバイくらい意味がよくわからない。&lt;/p&gt; &lt;p&gt;Razor使ってますか？使ってますよね。お腹すいたらRazorだし、テレビ見ながらRazorですよ！&lt;/p&gt; &lt;p&gt;疲れてるのかな．．．。&lt;/p&gt; &lt;h4&gt;~/Views/Shared/_Partial.cshtml&lt;/h4&gt;&lt;pre class="prettyprint"&gt;@{
    var message = Model ?? (PageData.Any() ? PageData.First().Value : null);
}
ゴールデン @message&lt;/pre&gt;
&lt;p&gt;↑こんな部分ビューを用意しました。&lt;/p&gt;
&lt;h4&gt;~/Views/Home/Index.cshtml&lt;/h4&gt;&lt;pre class="prettyprint"&gt;@{Layout = null;}
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="ja"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;title&amp;gt;連休&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;p&amp;gt;@@RenderPage - 
  @RenderPage("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.RenderPartial - 
  @{Html.RenderPartial("~/Views/Shared/_Partial.cshtml", "ウィーク");}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.Partial - 
  @Html.Partial("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;↑そしてそれを利用するビューも用意しました。&lt;/p&gt;
&lt;p&gt;ここで問題です！このページ(Index.cshtml)を表示するとどういう表示になるでしょーか？&lt;/p&gt;
&lt;h4&gt;答え&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_3YQxskhNNyI/TcOHcR5gPcI/AAAAAAAAB-g/WBEmP42c2YU/s1600-h/partial12.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="partial1" border="0" alt="partial1" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcOHcyGIFII/AAAAAAAAB-k/ipCzeYbXwLo/partial1_thumb.png?imgmax=800" width="240" height="155"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ビックリした？ねーねー、ビックリした？オレ、すげービックリした。&lt;/p&gt;
&lt;p&gt;なんでHtml.RenderPartialだと”ゴールデン ウィーク”と表示されないんでしょうね。ソース追っかけたりしてスゴイ悩んだんですよ。そもそもRazorではストリームに直接出力うするHtml.RenderPartial使えないのか！？とか仕様を疑ったり(RenderPartialの実装がMVC3になってTextWriter渡す実装に変わってるから関係ないのにね)。&lt;/p&gt;
&lt;p&gt;うーん、煮詰まった。ふと、順番入れ替えたらどうなるのか試してみたんす。&lt;/p&gt;&lt;pre class="prettyprint"&gt;&amp;lt;p&amp;gt;Html.RenderPartial - 
  @{Html.RenderPartial("~/Views/Shared/_Partial.cshtml", "ウィーク");}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.Partial - 
  @Html.Partial("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;@@RenderPage - 
  @RenderPage("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;@RenderPageを最後に移動。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcOHdqGeAHI/AAAAAAAAB-o/eAhRCyE_iTk/s1600-h/partial22.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="partial2" border="0" alt="partial2" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcOHd4PY28I/AAAAAAAAB-s/PCIp-E2HiOw/partial2_thumb.png?imgmax=800" width="240" height="155"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;おや～？ちゃんと出た。あれ～。この際Html.Partialは無視して試しに親戚のHtml.RenderActionはどうなるのか試してみたっす。&lt;/p&gt;
&lt;p&gt;HomeControllerに以下を追加。&lt;/p&gt;&lt;pre class="prettyprint"&gt;[ChildActionOnly]
public ActionResult Week()
{
    return Content("ウィーク");
}
&lt;/pre&gt;
&lt;p&gt;Index.cshtmlを以下のように変更(@RenderPageを最初に持って来てHtml.RenderAction追加)。&lt;/p&gt;&lt;pre class="prettyprint"&gt;&amp;lt;p&amp;gt;@@RenderPage - 
  @RenderPage("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.RenderPartial - 
  @{Html.RenderPartial("~/Views/Shared/_Partial.cshtml", "ウィーク");}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.RenderAction - 
  ゴールデン @{Html.RenderAction("Week");}&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcOHedzqvPI/AAAAAAAAB-w/v6fVv1awtCg/s1600-h/partial32.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="partial3" border="0" alt="partial3" src="http://lh6.ggpht.com/_3YQxskhNNyI/TcOHexnzn5I/AAAAAAAAB-0/dLWr1AXsTkk/partial3_thumb.png?imgmax=800" width="240" height="155"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Html.RenderActionは平気みたい。このまま@RenderPageを最後に移動。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;&amp;lt;p&amp;gt;Html.RenderPartial - 
  @{Html.RenderPartial("~/Views/Shared/_Partial.cshtml", "ウィーク");}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Html.RenderAction - 
  ゴールデン @{Html.RenderAction("Week");}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;@@RenderPage - 
  @RenderPage("~/Views/Shared/_Partial.cshtml", "ウィーク")&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TcOHfPc68-I/AAAAAAAAB-4/JVbZCBM7Cv8/s1600-h/partial4%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="partial4" border="0" alt="partial4" src="http://lh4.ggpht.com/_3YQxskhNNyI/TcOHfnb-22I/AAAAAAAAB-8/2lNfaAQE9Ic/partial4_thumb.png?imgmax=800" width="240" height="155"&gt;&lt;/a&gt;
&lt;p&gt;ちゃんと出ますね。ということはですよ、同じWebPage中では@RenderPageした後はHtml.RenderPartialが正しく動かないということですよ。これってもしかして．．．。あ、いや、仕様かもしれないし。どういう事？教えてWebMatrixMan～！&lt;/p&gt;
&lt;p&gt;※@RenderPage自体がRazor構文なのでWeb Formsでは関係ない問題です。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-7454442405168692471?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/hZ4wL1wYr1g" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/hZ4wL1wYr1g/renderpagehtmlrenderpartial.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_3YQxskhNNyI/TcOHcyGIFII/AAAAAAAAB-k/ipCzeYbXwLo/s72-c/partial1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/05/renderpagehtmlrenderpartial.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-4088881362466667770</guid><pubDate>Wed, 04 May 2011 07:59:00 +0000</pubDate><atom:updated>2011-05-04T16:59:54.960+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET MVC</category><title>ぷっすぷすにしてやんよ、Glimpseで。</title><description>&lt;p&gt;&lt;a href="http://getglimpse.com/"&gt;Glimpse - A client side look at whats going on in your server&lt;/a&gt;&lt;/p&gt; &lt;p&gt;なんと素敵なツールでしょうね！&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TcEHUYJDIeI/AAAAAAAAB9A/9I1EVKrdklw/s1600-h/glimpse1%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse1" border="0" alt="glimpse1" src="http://lh6.ggpht.com/_3YQxskhNNyI/TcEHU35Zm_I/AAAAAAAAB9E/Im7ZNyuibtw/glimpse1_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;↑ChromeでGlimpseをオンにしたうえに、デベロッパーツールもオンにした状態。かぶせまくり。&lt;/p&gt; &lt;p&gt;これ何？っていうのは上記Glimpseサイトのビデオか、MIX11でのHanselmanさんのセッションビデオ見れば一発でわかります。と言ってしまうとブログに書く必要が無くなってしまうので、少し説明すると、Trace.axdやelmah.axd、RouteDebuggerを全部ひっくるめて組み合わせてさらにサーバーサイドの情報をひと通り参照できるようにするツールデス。分かりにくいっすね。&lt;/p&gt; &lt;p&gt;とにかく、NuGetでInstall-Package Glimpseと入力してプロジェクトに入れて見ましょう。&lt;/p&gt; &lt;p&gt;その状態でデバッグを開始すると、おや？ナニも変化ないですね。なので、URLに/Glimpse/ConfigといれてGlimpse機能をOnにしまいしょう。Onにした後はURLをもとのページに自分で戻すことを忘れずに。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHVJmGapI/AAAAAAAAB9I/8GmjhYjQVs0/s1600-h/glimpse2%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse2" border="0" alt="glimpse2" src="http://lh5.ggpht.com/_3YQxskhNNyI/TcEHVhNh8CI/AAAAAAAAB9M/WsYKKnwuAlw/glimpse2_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;そうすると画面右下に変な目玉アイコン(Glimpseアイコンね)が表示されるのでそこをクリック！&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TcEHWK4sS4I/AAAAAAAAB9Q/hEoXVY7lOLQ/s1600-h/glimpse3%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse3" border="0" alt="glimpse3" src="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHWTp5LGI/AAAAAAAAB9U/pn6tKxBzbeo/glimpse3_thumb.png?imgmax=800" width="240" height="169"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;そうすると出てきます。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHXem59wI/AAAAAAAAB9Y/iatFXTPRpOM/s1600-h/glimpse4%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse4" border="0" alt="glimpse4" src="http://lh5.ggpht.com/_3YQxskhNNyI/TcEHXg7pePI/AAAAAAAAB9c/XbbbVbZE8-I/glimpse4_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;すごいねー。ちなみにブラウザに表示されてるのはプラグインとかじゃなくて100％JavaScriptだけで作られたものです。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHYKRGCuI/AAAAAAAAB9g/UIkvSznfj0Q/s1600-h/glimpse5%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse5" border="0" alt="glimpse5" src="http://lh6.ggpht.com/_3YQxskhNNyI/TcEHYwRNvSI/AAAAAAAAB9k/LgXiLTUdzck/glimpse5_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&amp;nbsp;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHZJXFwTI/AAAAAAAAB9o/al5_D323fPk/s1600-h/glimpse6%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="glimpse6" border="0" alt="glimpse6" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHZoPLu8I/AAAAAAAAB9s/8C6bMaLrGFo/glimpse6_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ソース表示すると&amp;lt;/html&amp;gt;の後にデータがレンダリングされて、最後にglimpseClinet.jsがロードされてるのが確認できます。&lt;/p&gt; &lt;p&gt;あとは、見たまんまです。各タブにそれぞれ情報が表示されます。&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Binding&lt;br&gt;まだ開発中～。Binderの情報がでる予定(だと思います)  &lt;li&gt;Config&lt;br&gt;Web.Configの設定内容をセクション別に表示&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHZxlWbCI/AAAAAAAAB9w/8z6zeB657IA/s1600-h/config%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="config" border="0" alt="config" src="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHafQR93I/AAAAAAAAB90/Z_OttzVsBhk/config_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;Environment&lt;br&gt;サーバーの実行環境の情報&lt;br&gt;Application Assemblies/System Assembliesが見れるのは嬉しいですね&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHa5ueGlI/AAAAAAAAB94/6jF79oaoSlA/s1600-h/emvironment%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="emvironment" border="0" alt="emvironment" src="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHbLN6lAI/AAAAAAAAB98/iDppL73phvs/emvironment_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;Execution&lt;br&gt;ActionInvokderの実行内容を表示(だと思う)&lt;br&gt;Filterの実行も見れるよ！&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHbpAvKlI/AAAAAAAAB-A/CBeXMKOh4Uw/s1600-h/execution%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="execution" border="0" alt="execution" src="http://lh5.ggpht.com/_3YQxskhNNyI/TcEHbxpQnhI/AAAAAAAAB-E/ffIn4ee0ltg/execution_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;Glimpse Warnings&lt;br&gt;Glimpseの何か  &lt;li&gt;MetaData&lt;br&gt;どこぞのMetadata  &lt;li&gt;Remote&lt;br&gt;リクエスト履歴っぽい  &lt;li&gt;Request&lt;br&gt;HttpRequestBaseの中身からCookie,Form,QueryStringを表示&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHcdK87rI/AAAAAAAAB-I/lMJpRt83Pzw/s1600-h/request%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="request" border="0" alt="request" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHcm1DDCI/AAAAAAAAB-M/fQl50ttSHUo/request_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;Routes&lt;br&gt;表示内容(アクション)に到達するルーティングの解決順とマッチしたルート&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHdC5doJI/AAAAAAAAB-Q/rErJGwzN78M/s1600-h/route%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="route" border="0" alt="route" src="http://lh4.ggpht.com/_3YQxskhNNyI/TcEHdfhxUQI/AAAAAAAAB-U/s9ZaiwLv4_s/route_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;Server&lt;br&gt;ServerValiablesの内容  &lt;li&gt;Session&lt;br&gt;Sessionの内容  &lt;li&gt;Trace&lt;br&gt;System.Diagnostics.Traceに出力した内容  &lt;li&gt;Views&lt;br&gt;表示内容(View)の解決に至る過程&lt;br&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHeCYx-6I/AAAAAAAAB-Y/gr02HZ9NP48/s1600-h/view%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="view" border="0" alt="view" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcEHeQP4vsI/AAAAAAAAB-c/u5gQVZA1Oiw/view_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;  &lt;li&gt;XHRequest&lt;br&gt;まだっぽい&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;全部スクリーンショットをとるのが面倒だったから動かして確認してみてね！&lt;/p&gt; &lt;p&gt;んじゃ、そもそもGlimpseの仕組みはどうなってるの？って気になるよね。NuGetからインストールすると使うのは楽チンだけどソースが確認できないから、改めてソースをダウンロードしましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="https://github.com/Glimpse/Glimpse/"&gt;Glimpse/Glimpse - GitHub&lt;/a&gt;&lt;/p&gt; &lt;p&gt;真っ先に気になるのが、PreApplicationStartCode。当たり前のようにスタートアップコードが書かれてる。んで、その中身はMicrosoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModuleを使ったカスタムIHttpModuleの動的登録。その中で各種プラグイン(タブで表示する情報を取得したりするもの)をMEFで登録。&lt;/p&gt; &lt;p&gt;Moduleでハンドリングするタイミングは以下の４つ。&lt;/p&gt; &lt;p&gt;context.BeginRequest += BeginRequest;&lt;br&gt;context.EndRequest += EndRequest;&lt;br&gt;context.PostRequestHandlerExecute += PostRequestHandlerExecute;&lt;br&gt;context.PreSendRequestHeaders += PreSendRequestHeaders;&lt;/p&gt; &lt;p&gt;ここから結構気になってるExecutionで表示してるのがホントにActionInvokerに介入した結果なのかチェックしてみよう。&lt;/p&gt; &lt;p&gt;ExecutionプラグインのSetupInitでいろいろ仕込んでますね。ControllerFactoryとDependencyResolverをGlimpseのものに置き換えてます。GlimpseControllerFactory.CreateControllerでIControllerExtentions.TrySetActionInvokerを呼んでCastleのInterceptorを利用して、プロキシを生成しそれをActionInvokerにセットしてました。これでプロキシが必ず呼び出されるから、実行をインターセプトして実行情報を取得できるということですね。うほ。&lt;/p&gt; &lt;p&gt;DependencyResolverを利用した場合にも大丈夫なようにGlimpseDependencyResolverが用意されてて、こっちでも同じくProxyを生成するように抜かりなし。&lt;/p&gt; &lt;p&gt;他のプラグインもIGlimpsePlugin実装になってて、SetupInitしてGetDataを呼ぶことで各種データをHttpContext.Itemsに入れることで、最終的にGlimpseRespondersがJsonにシリアライズ。まるっとApplication編集に保存してGlimpseResponders.StandardResponseがResponse.Writeしてる。&lt;/p&gt; &lt;h3&gt;Glimpseの仕組み&lt;/h3&gt; &lt;p&gt;MEFを使ったプラグイン構造でContext.ItemsからApplication変数にデータを保持(configのsaveRequestCountはこの履歴保持数ということみたい)し、それをレスポンス(リクエスト毎にGuidを発行してコンテキストがきちんと判定できるよう保持)。各種情報はプラグインが自立して取得するけど、実行状態のインターセプトはCastleがProxyを生成して介入。&lt;/p&gt; &lt;p&gt;イロイロなプラグインを作っていけば、データの取得と表示はベースの機能でまかなえる素敵設計であるのと、.NET4、MVC3の機能もめいいっぱい利用したお洒落実装。これは萌える。萌え萌え。ぷっすぷすにされちまったぜ！&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-4088881362466667770?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/qYd491J5SJg" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/qYd491J5SJg/glimpse.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_3YQxskhNNyI/TcEHU35Zm_I/AAAAAAAAB9E/Im7ZNyuibtw/s72-c/glimpse1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/05/glimpse.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-7170341112370887915</guid><pubDate>Tue, 03 May 2011 14:45:00 +0000</pubDate><atom:updated>2011-05-03T23:45:54.160+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">LINQ to SQL</category><category domain="http://www.blogger.com/atom/ns#">C#</category><title>Dapper.NET</title><description>&lt;p&gt;&lt;a href="http://code.google.com/p/dapper-dot-net/"&gt;dapper-dot-net - Simple SQL object mapper for SQL Server - Google Project Hosting&lt;/a&gt;&lt;/p&gt; &lt;p&gt;これ。Massiveと同じくらい短いコードのORM。&lt;a href="http://msdn.microsoft.com/en-us/library/gg569069(v=VS.99).aspx"&gt;WebMatrix.Data.Database&lt;/a&gt;みたいなものですね。&lt;/p&gt; &lt;p&gt;オープンソースとしてたくさん存在する軽量ORMのなかでもDapperがスゴイところがパフォーマンス。どのくらい早いのかは上記サイトを確認してみると各種ORMとの速度比較が出てます。&lt;/p&gt; &lt;p&gt;&lt;a href="http://samsaffron.com/archive/2011/05/02/A+day+in+the+life+of+a+slow+page+at+Stack+Overflow"&gt;A day in the life of a slow page at Stack Overflow&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ASP.NET MVC &amp;amp; SQLServer &amp;amp; LINQ to SQLのスタックで有名な大規模サイトといえばStackoverflow.comですね。なんとそのStackoverflow.comでパフォーマンスに問題が出てきたので解決するために、このDapperを利用したというじゃないですか。&lt;/p&gt; &lt;p&gt;そもそもORMでN+1問題が出やすいので、LINQならjoin使ってたくさんのSQLを発行しないようにするとか、DBとのあいだのやりとりもちゃんと確認する(パフォーマンスに問題があると認識したならの話です。問題になってないなら気にしなくていいですよ)必要がありましょう。その辺はちゃんと実装したとしてもDapperが早いということです。Emitしちゃってるし。&lt;/p&gt; &lt;p&gt;使い方も簡単でIDbConnectionの拡張メソッドとして実装してるので、DBコネクションさえあれば簡単に導入できます。もちろんモデルクラスをPOCOで定義しておくことが最速を維持する秘訣。&lt;/p&gt; &lt;p&gt;AdventureWorksLT2008R2をDBに利用するためのサンプル。&lt;/p&gt;&lt;pre class="prettyprint"&gt;public class Product
{
  public int ProductId { get; set; }
  public string Name { get; set; }
  public string ProductNumber { get; set; }
}

using (var connection = connectionFactory())
{
  var result = connection.Query&amp;lt;int&amp;gt;("select count(*) from Product").Single();
  Console.WriteLine(result + " products.");
}

// 1テーブルを1クラスにマッピング
using (var connection = connectionFactory())
{
  var result =
      connection.Query&amp;lt;Product&amp;gt;(
          "select * from Product where ListPrice between @low and @high",
          new { low = 10.0, high = 100.0 });
  Console.WriteLine("-- simple mapping:" + result.Count());
  foreach (var p in result)
  {
      Console.WriteLine(string.Format("ID:{0},Name:{1}", p.ProductId, p.Name));
  }
}

// ジェネリック指定しないで1テーブルをマッピング
using (var connection = connectionFactory())
{
  var result =
      connection.Query(
          "select * from Product where ListPrice between @low and @high",
          new { low = 10.0, high = 100.0 });
  Console.WriteLine("-- dynamic mapping:" + result.Count());
  foreach (var p in result)
  {
      Console.WriteLine(string.Format("ID:{0},Name:{1},Price:{2}", p.ProductID, p.Name, p.ListPrice));
  }
}
&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcAVG8OQWzI/AAAAAAAAB8w/vYFbgxkTWv4/s1600-h/dapper2%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="dapper2" border="0" alt="dapper2" src="http://lh3.ggpht.com/_3YQxskhNNyI/TcAVHTgpeXI/AAAAAAAAB80/NcafTn4RB0c/dapper2_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ここで、実際にテーブルにはたくさん項目あるけど、モデルには省略しちゃってるんですけど、このままSQLにjoin含めた場合、ちゃんとモデルにはマッピングできませんでした。dynamicだとうまくいくんだけど、その辺の整合性はちゃんととっておかないとダメなわけですね。でもdynamicな戻り値なら早いわけではない。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TcAVH1FEMkI/AAAAAAAAB84/Qdw7big0W8M/s1600-h/dapper12.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="dapper1" border="0" alt="dapper1" src="http://lh5.ggpht.com/_3YQxskhNNyI/TcAVIcaTLaI/AAAAAAAAB88/pClyaTndac4/dapper1_thumb.png?imgmax=800" width="240" height="94"&gt;&lt;/a&gt;&lt;/p&gt;&lt;pre class="prettyprint"&gt;var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i &amp;lt; 100; i++)
{
  using (var connection = connectionFactory())
  {
    var result =
        connection.Query(
            @"select p.*, c.ProductCategoryId as CategoryId, c.Name as CategoryName
          from Product as p 
            inner join ProductCategory as c on 
              p.ProductCategoryID = c.ProductCategoryID 
          where p.ListPrice between @low and @high",
            new { low = 10.0, high = 100.0 });
    foreach (var p in result){}
  }
}
stopwatch.Stop();
Console.WriteLine("dapper time:" + stopwatch.ElapsedMilliseconds + " ms");

stopwatch.Reset();
stopwatch.Start();
for (var i = 0; i &amp;lt; 100; i++)
{
  using (var l2s = new AwDataContext(connectionFactory()))
  {
    var result = from p in l2s.Product
                 join c in l2s.ProductCategory on p.ProductCategoryID equals c.ProductCategoryID
                 select
                     new
                         {
                             ProductId = p.ProductID,
                             p.Name,
                             Category = c.Name
                         };
    foreach (var p in result) {}
  }
}
stopwatch.Stop();
Console.WriteLine("linq2sql time:" + stopwatch.ElapsedMilliseconds + " ms");
&lt;/pre&gt;
&lt;p&gt;大きなサイトへ導入されてるというのは、興味深いじゃないですか。それだけの負荷で問題が起きないように丁寧な実装がされてるわけで(どっちもだけど)。&lt;/p&gt;
&lt;p&gt;今後、より使いやすくなることを期待しつつ見守っていきたいプロジェクトです。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-7170341112370887915?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/fyIZswOmhts" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/fyIZswOmhts/dappernet.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_3YQxskhNNyI/TcAVHTgpeXI/AAAAAAAAB80/NcafTn4RB0c/s72-c/dapper2_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/05/dappernet.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-709178655867155293</guid><pubDate>Sat, 23 Apr 2011 15:32:00 +0000</pubDate><atom:updated>2011-04-24T00:35:18.874+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><title>IIS ExpressでSSLを有効にしてデバッグ</title><description>&lt;p&gt;&lt;a href="http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx?utm_source=feedburner&amp;amp;utm_medium=feed&amp;amp;utm_campaign=Feed%3A+ScottHanselman+%28Scott+Hanselman+-+ComputerZen.com%29"&gt;Working with SSL at Development Time is easier with IISExpress - Scott Hanselman&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Hanselmanさんのエントリに書かれてる通りデス！  &lt;p&gt;IIS ExpressでSSLを有効にしたデバッグだと無効な証明書だと警告はでるけど、HTTPSでのテストはコレでできますよね。  &lt;p&gt;だけど、名前はlocalhostに限定です。これはまぁ、そういうバインドしかapplicationHost.configに登録してないからなのと、アクセス許可をしてなからなんですが(詳しくは&lt;a href="http://channel9.msdn.com/Series/mvcConf/mvcConf-2-Vaidy-Gopalakrishnan-IIS-Express"&gt;mvcConf 2 - Vaidy Gopalakrishnan: IIS Express | mvcConf | Channel 9&lt;/a&gt;)、その辺をちゃんと登録して気持ちよく使いましょうという内容ですね。途中URLRewriteでAccountControllerへのアクセスをHTTPSに書き換えるというのが挟まってるけど、コードで対応するならここは飛ばしてもOKです。  &lt;p&gt;netshへのcerthashの登録、信頼されたルート証明機関の証明書として証明書を登録、最終的にIisExpressAdminCmdでACLの許可登録で気持よく、マシン名でもSSL有効な状態でアクセスできます。  &lt;p&gt;というのを、自分の環境でも確認してみましょう。&lt;/p&gt; &lt;h3&gt;１．ASP.NET MVCサイトを作成&lt;/h3&gt; &lt;p&gt;もちろんVS2010で行うのが簡単ぽんなんですが、VS2008でも同じようにできるので(applicationHost.configとnetshなので)、そっちしかない場合はそっちで。プロジェクトの設定で、外部プログラムを起動するを利用しましょう(&lt;a href="http://takepara.blogspot.com/2011/01/iis-expressvs2008vs2010.html"&gt;IIS ExpressのVS2008/VS2010利用&lt;/a&gt;)。&lt;/p&gt; &lt;p&gt;今回は簡単なVS2010で新しくなったMVC3 Toolsで作ります。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw3QZGEqI/AAAAAAAAB60/kWeVGdFEvdo/s1600-h/ssl1%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl1" border="0" alt="ssl1" src="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw35KYh7I/AAAAAAAAB64/KUfNly69cIY/ssl1_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;いい感じにHTML5ですね！&lt;/p&gt; &lt;h3&gt;２．プロジェクトのプロパティでSSL有効化&lt;/h3&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw4RfyBuI/AAAAAAAAB68/R82z0in6zf8/s1600-h/ssl2%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl2" border="0" alt="ssl2" src="http://lh3.ggpht.com/_3YQxskhNNyI/TbLw45xZFYI/AAAAAAAAB7A/Pm8bpLD4wdM/ssl2_thumb.png?imgmax=800" width="210" height="240"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;trueにするだけの簡単な作業です。これでSSL URLが入ります。この例だと &lt;a href="https://localhost:44301/"&gt;https://localhost:44301/&lt;/a&gt; ですね。&lt;/p&gt; &lt;p&gt;この状態でまずは実行してみましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TbLw5Y5LtwI/AAAAAAAAB7E/RhbEqBdb9ho/s1600-h/ssl3%5B5%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl3" border="0" alt="ssl3" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw5wtiRaI/AAAAAAAAB7I/SdKnaQKa5pM/ssl3_thumb%5B1%5D.png?imgmax=800" width="225" height="240"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;文句は言われるけど、普通に問題なくアクセスできますね。楽チンデス。IIS Express最高！&lt;/p&gt; &lt;h3&gt;３．localhostじゃなく好きな名前でアクセス&lt;/h3&gt; &lt;p&gt;マシン名でもいいんですが、そこはほら、恥ずかしい名前付けてる場合もあるじゃないですか。そんな状態で表示するのもどうかと思うわけですよ。なので、今回は完全カスタムな名前にしてしまいます。&lt;/p&gt; &lt;p&gt;takepara-local&lt;/p&gt; &lt;p&gt;という名前にします。これが恥ずかしい名前じゃないかと言われると、まぁ微妙ですが。あ、ちなみに自分のマシン名はそんなに恥ずかしい名前じゃないですよ。&lt;/p&gt; &lt;p&gt;IisExpressAdminCmdでアクセスできるようにしましょう。&lt;/p&gt; &lt;p&gt;※”.”でドメインな感じのマシン名だとsetupFriendlyHostnameUrlに失敗します。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw6MttlSI/AAAAAAAAB7M/nJM6U1utPvU/s1600-h/ssl6%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl6" border="0" alt="ssl6" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw6lIrutI/AAAAAAAAB7Q/V_OSreNX-cg/ssl6_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;これでブラウザからアクセスしてみましょう！&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw6yWUhnI/AAAAAAAAB7U/vC2SZv_AwVA/s1600-h/ssl7-2%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl7-2" border="0" alt="ssl7-2" src="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw7ShGaII/AAAAAAAAB7Y/i3kP8EgUz98/ssl7-2_thumb.png?imgmax=800" width="240" height="142"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;残念。エラーです。何か忘れてますね。そうですね、applocationHost.configにバインド登録してなかったですね。&lt;/p&gt; &lt;p&gt;&amp;lt;site name="SslTest" id="15"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;application path="/" applicationPool="Clr4IntegratedAppPool"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;virtualDirectory path="/" physicalPath="..." /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/application&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bindings&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding protocol="http" bindingInformation="*:51578:localhost" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding protocol="https" bindingInformation="*:44301:localhost" /&amp;gt;&lt;br&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding protocol="http" bindingInformation="*:80:takepara-local" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;binding protocol="https" bindingInformation="*:443:takepara-local" /&amp;gt;&lt;br&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/bindings&amp;gt;&lt;br&gt;&amp;lt;/site&amp;gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_3YQxskhNNyI/TbLw7mtUqgI/AAAAAAAAB7c/IP0VIkRxR3g/s1600-h/ssl7%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl7" border="0" alt="ssl7" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw8EhR43I/AAAAAAAAB7g/3413dB5sgVQ/ssl7_thumb.png?imgmax=800" width="240" height="157"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;グレートですね。ちなみにhostsにtakepara-local登録して、netshでurlacl登録しても一緒です。&lt;/p&gt;  &lt;p&gt;このまま、調子にのってHTTPSにしてみる。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw8vcrvSI/AAAAAAAAB7k/Yy_OHFpcRis/s1600-h/ssl8%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl8" border="0" alt="ssl8" src="http://lh5.ggpht.com/_3YQxskhNNyI/TbLw841_GaI/AAAAAAAAB7s/RiUxI-elqkk/ssl8_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;残念。ダメでした。&lt;/p&gt; &lt;h3&gt;４．証明書を作成&lt;/h3&gt; &lt;p&gt;takepara-localの証明書を作成してませんでしたね。hanselmanさんのエントリに書かれてるとおりです。makecertしましょう。CNだけ変えて後はそのままで。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw9dzwM8I/AAAAAAAAB7w/vJg-_A6aR34/s1600-h/ssl9%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl9" border="0" alt="ssl9" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw99eCYQI/AAAAAAAAB70/2aBJENimbjM/ssl9_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;makecert -r -pe -n "CN=TAKEPARA-LOCAL" -b 01/01/2000 -e 01/01/2036 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12&lt;/p&gt; &lt;p&gt;VSコマンドプロンプトから実行しましょう。パス通ってて確実。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw-d0oj9I/AAAAAAAAB74/59sAtw-E-LE/s1600-h/ssl10%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl10" border="0" alt="ssl10" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw-yGKmHI/AAAAAAAAB78/XrvgPGC9XtE/ssl10_thumb.png?imgmax=800" width="240" height="139"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;証明書をnetshで登録。&lt;/p&gt; &lt;p&gt;netsh http add sslcert ipport=0.0.0.0:443 appid={214124cd-d05b-4309-9af9-9caa44b2b74a} certhash=864759f75a04d030e243ecf1cfe19ce336653374&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_3YQxskhNNyI/TbLw_bAZk4I/AAAAAAAAB8A/uiiWC8b9fRk/s1600-h/ssl11%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl11" border="0" alt="ssl11" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLw_9q6bkI/AAAAAAAAB8E/BvN4YTKndAQ/ssl11_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;a href="http://lh5.ggpht.com/_3YQxskhNNyI/TbLxAdMtmwI/AAAAAAAAB8I/x0MCD7XhLFs/s1600-h/ssl12%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl12" border="0" alt="ssl12" src="http://lh6.ggpht.com/_3YQxskhNNyI/TbLxAtRdD3I/AAAAAAAAB8M/nRQCdNjPl2o/ssl12_thumb.png?imgmax=800" width="240" height="143"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;警告でるけど、気にせず表示。&lt;/p&gt; &lt;h3&gt;５．証明書を信頼&lt;/h3&gt; &lt;p&gt;このままでもいいんですが、やっぱり気持よく。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/TbLxBIA8qsI/AAAAAAAAB8Q/c2ebHIvfmiA/s1600-h/ssl13%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl13" border="0" alt="ssl13" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLxBq5LseI/AAAAAAAAB8U/GZHgGhY0Ib0/ssl13_thumb.png?imgmax=800" width="240" height="139"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;信頼しちゃいましょう。自分のマシンで、自分で作った証明書。信頼しちゃえばいいじゃないか！&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TbLxCOr_kGI/AAAAAAAAB8Y/cFAUjUwo9FI/s1600-h/ssl14%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl14" border="0" alt="ssl14" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLxCtY3aDI/AAAAAAAAB8c/3EA2Pq0vrFU/ssl14_thumb.png?imgmax=800" width="240" height="160"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Chrome厳しす。まぁ、いいか。警告でなくなったしね！&lt;/p&gt; &lt;p&gt;６．Accountコントローラを厳しく&lt;/p&gt; &lt;p&gt;URLRewriteで同じようになってもいいんですが、あえてコードで対応しましょう。と、言っても難しいことはなく、AccountControllerにRequireHttps属性を付けるだけ。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/TbLxC_nkgBI/AAAAAAAAB8g/8Ckxwcd6LrE/s1600-h/ssl15%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl15" border="0" alt="ssl15" src="http://lh3.ggpht.com/_3YQxskhNNyI/TbLxDRcfxRI/AAAAAAAAB8k/l6JbdWx_rx0/ssl15_thumb.png?imgmax=800" width="240" height="221"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;これで、AccountControllerへのアクセスがHTTPSになってなければ、リダイレクトされます。リダイレクトされるということはHTTPでのPOSTは無視される(リダイレクトはGETデス)ので、気をつけましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_3YQxskhNNyI/TbLxD_Z_YrI/AAAAAAAAB8o/AzAM8ynt0ss/s1600-h/ssl16%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="ssl16" border="0" alt="ssl16" src="http://lh4.ggpht.com/_3YQxskhNNyI/TbLxEfnocJI/AAAAAAAAB8s/myN8JH-WsTQ/ssl16_thumb.png?imgmax=800" width="240" height="181"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;この動きが気持ち悪いときはViewでURLを埋め込むときにHTTPSにしてあげる必要があるので、修正箇所が随分増えちゃいますが、通常の開発であればViewでもちゃんと書いて、ControllerへのRequireHttpsも付ける2段構えになるのが王道でしょう。抜け道なし。ViewのURLなんて嘘つくからね。&lt;/p&gt; &lt;p&gt;例えば、_LogOnPartial.cshtmlを以下のようにする。&lt;/p&gt;&lt;pre class="prettyprint"&gt;@if(Request.IsAuthenticated) {
    &amp;lt;text&amp;gt;Welcome &amp;lt;strong&amp;gt;@User.Identity.Name&amp;lt;/strong&amp;gt;!
    [ @Html.ActionLink("Log Off", "LogOff", "Account", "https", "takepara-local", "", null, null) ]&amp;lt;/text&amp;gt;
}
else {
    @:[ @Html.ActionLink("Log On", "LogOn", "Account", "https", "takepara-local", "", null, null) ]
}
&lt;/pre&gt;
&lt;p&gt;これで、必ずHTTPSのURLが生成されます。&lt;/p&gt;
&lt;p&gt;画像てんこ盛りですが、手順通りやればうまくいくので、HTTPSでのテストをやりたい場合は是非どうぞ。&lt;/p&gt;
&lt;p&gt;なんか、うまくできない！という場合はタスクトレイのIIS Expressを終了させてやり直してみるといいかも。applicationHost.config書き換えしたときなんかは特に。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-709178655867155293?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/cVdOO9eDlpM" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/cVdOO9eDlpM/iis-expressssl.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_3YQxskhNNyI/TbLw35KYh7I/AAAAAAAAB64/KUfNly69cIY/s72-c/ssl1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/04/iis-expressssl.html</feedburner:origLink></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-292244481221667867.post-374056604547007176</guid><pubDate>Sun, 17 Apr 2011 10:21:00 +0000</pubDate><atom:updated>2011-04-19T04:08:34.581+09:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ごにょごにょ</category><category domain="http://www.blogger.com/atom/ns#">遊び</category><title>MIX11をChannel9からまとめどり</title><description>&lt;p&gt;更新：2011年4月19日 4:07&lt;/p&gt; &lt;p&gt;残念なお知らせと、うれしいお知らせがあります。&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.hanselman.com/blog/Mix11VideosDownloadThemAllWithRSS.aspx"&gt;Mix 11 Videos - Download them all with RSS - Scott Hanselman&lt;/a&gt;&lt;/p&gt; &lt;p&gt;残念なのからいくと、このエントリは用なしになりました。嬉しいのは公式にMP4 Highが配信されました。&lt;/p&gt; &lt;p&gt;ダウンロードしなおすぜ！頑張れオレ！&lt;/p&gt; &lt;p&gt;&lt;a href="http://channel9.msdn.com/Events/MIX/MIX11"&gt;MIX11 | Channel 9&lt;/a&gt;&lt;/p&gt; &lt;p&gt;いっぱいですね～。120個くらいですかね。MP4だけダウンロードしてiPadに入れて通勤時に全部目を通しておくのが、ラスベガスに行けなかったマニアのおつな楽しみ方というものでしょう。&lt;/p&gt; &lt;p&gt;&lt;a title="http://channel9.msdn.com/Events/MIX/MIX11/RSS" href="http://channel9.msdn.com/Events/MIX/MIX11/RSS"&gt;http://channel9.msdn.com/Events/MIX/MIX11/RSS&lt;/a&gt;&lt;/p&gt; &lt;p&gt;とはいえ、Feedから取り出せたりするほど、便利なわけでもなく。やり方あるのかな？&lt;/p&gt; &lt;p&gt;でもそんなこと気にしない。ListタブをクリックすればすべてのMP4動画へのリンクがありますね。ということは、HTMLにはちゃんと動画のURLが埋まってるわけですね。となれば、スクレイピング！&lt;/p&gt; &lt;p&gt;なんてね。今回はちょっと趣向を変えてIE9のコンソールでJavaScriptを実行して取り出してしまいましょう！と、思ったけど、なんかちょっと思ったような出力が得られなかったので、Chromeで取り出してしまいましょう！てへ。&lt;/p&gt; &lt;p&gt;まずはChromeでページにアクセス。&lt;/p&gt; &lt;p&gt;&lt;a href="http://channel9.msdn.com/Events/MIX/MIX11?sort=status&amp;amp;direction=asc#tab_sortBy_status"&gt;http://channel9.msdn.com/Events/MIX/MIX11?sort=status&amp;amp;direction=asc#tab_sortBy_status&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/Taq_AGfp7eI/AAAAAAAAB6E/qXnpM8zvkGE/s1600-h/mix11-1%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-1" border="0" alt="mix11-1" src="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_AiNeD0I/AAAAAAAAB6I/zHmSz2O3ctM/mix11-1_thumb.png?imgmax=800" width="240" height="181"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Shift+Ctrl+iでコンソールをひらきましょう。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/Taq_BUEGUvI/AAAAAAAAB6M/8ZZb1fDB5K8/s1600-h/mix11-2%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-2" border="0" alt="mix11-2" src="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_B08J5dI/AAAAAAAAB6Q/jixVhe-N6w4/mix11-2_thumb.png?imgmax=800" width="240" height="181"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;MIX11のページにはjQueryが読み込まれてるので、やりたい放題ですよ。まずは変数の初期化。&lt;/p&gt; &lt;p&gt;var list=[];&lt;/p&gt; &lt;p&gt;と入れて改行。&lt;/p&gt; &lt;p&gt;続いて、listにエレメントから取り出したMP4一覧をタイトル込みで入れる。&lt;/p&gt; &lt;p&gt;$(".entry-meta &amp;gt; .downloads a[href*='.mp4']").each(function(i,v){list.push("wget -O \""+$(v).parents(".entry-meta").find(".title").text().replace(":","-").replace("?","？") +".mp4\" "+ $(v).attr("href"));});&lt;/p&gt; &lt;p&gt;↑これをコピペして改行すると、ぞろぞろ表示されます。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_3YQxskhNNyI/Taq_CZZCYGI/AAAAAAAAB6U/4-QUgu_VxgA/s1600-h/mix11-3%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-3" border="0" alt="mix11-3" src="http://lh6.ggpht.com/_3YQxskhNNyI/Taq_DFqD3gI/AAAAAAAAB6Y/2i2af0-CI68/mix11-3_thumb.png?imgmax=800" width="240" height="181"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;が、結果はlistに格納されてるので、続けて&lt;/p&gt; &lt;p&gt;list&lt;/p&gt; &lt;p&gt;と、入力して改行。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_3YQxskhNNyI/Taq_Dh937QI/AAAAAAAAB6c/IQy5dNpILwk/s1600-h/mix11-4%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-4" border="0" alt="mix11-4" src="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_EOnQn9I/AAAAAAAAB6g/PjBSPPviL3s/mix11-4_thumb.png?imgmax=800" width="240" height="181"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;あとはコレをさらにコピペして、都合のいいように編集してコンソールからwget！curlでもいいっす。ちなみに複数の解像度のファイルがあるので、タイトルだけでファイル名にすると重複しちゃうので、その辺は目視で変更。さらにファイルとして使えない文字もあったりするので、ちょこっと修正。お好みに合わせて、どーぞー。ただいまダウンロード中の図。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_ETavu-I/AAAAAAAAB6k/-FRBu-3zN-Q/s1600-h/mix11-6%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-6" border="0" alt="mix11-6" src="http://lh5.ggpht.com/_3YQxskhNNyI/Taq_E-k_EbI/AAAAAAAAB6o/ANRGoyk4daI/mix11-6_thumb.png?imgmax=800" width="240" height="174"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;iframe style="padding-bottom: 0px; background-color: #fcfcfc; padding-left: 0px; width: 98px; padding-right: 0px; height: 115px; padding-top: 0px" title="Preview" marginheight="0" src="http://cid-df0c51db6ad31f29.office.live.com/embedicon.aspx/.Public/mix11.cmd" frameborder="0" marginwidth="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;/p&gt; &lt;p&gt;※パスは都合のいいように書き換えてね！&lt;/p&gt; &lt;p&gt;ちなみにIEで実行したのが↓こちら。&lt;/p&gt; &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_FZmzU8I/AAAAAAAAB6s/jmNdB3Nhi6Q/s1600-h/mix11-5%5B2%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="mix11-5" border="0" alt="mix11-5" src="http://lh6.ggpht.com/_3YQxskhNNyI/Taq_Fgq18eI/AAAAAAAAB6w/oQLfL8slErQ/mix11-5_thumb.png?imgmax=800" width="240" height="173"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;ちょっとコピペしにくい。というかできない。&lt;/p&gt; &lt;p&gt;これで見放題ですな。ぐへへ、ぐへへへ～。たまりませんな～。あ、でも解像度低いとiPadでちょっと見づらい。文字潰れ気味。スライドはギリセーフだけど、コードが見れるかどうかが大事なので、ダウンロード後にちょっと確認しないとね。&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/292244481221667867-374056604547007176?l=takepara.blogspot.com' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/jNRCtw/~4/5QQDfd3q0yA" height="1" width="1"/&gt;</description><link>http://feedproxy.google.com/~r/blogspot/jNRCtw/~3/5QQDfd3q0yA/mix11channel9.html</link><author>noreply@blogger.com (たけはら)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_3YQxskhNNyI/Taq_AiNeD0I/AAAAAAAAB6I/zHmSz2O3ctM/s72-c/mix11-1_thumb.png?imgmax=800" height="72" width="72" /><feedburner:origLink>http://takepara.blogspot.com/2011/04/mix11channel9.html</feedburner:origLink></item></channel></rss>

