ASP.NET MVC 処理順番
Posted in ASP.NET on 10月 18th, 2011 by Site Administratorcontrollerクラスの処理順番は下記の通り
1.Execute
2.OnAuthorization
3.OnActionExecuting
4.指定コントローラの指定アクションメソッド
5.View
6.OnActionExecuted
7.OnResultExecuting
8.OnResultExecuted
controllerクラスの処理順番は下記の通り
1.Execute
2.OnAuthorization
3.OnActionExecuting
4.指定コントローラの指定アクションメソッド
5.View
6.OnActionExecuted
7.OnResultExecuting
8.OnResultExecuted
ASP.NETではSessionIDの再発行処理が扱いにくい(タイミングがAbandonを実行した次のリクエスト時になる)
ということもあり、フィクセーション対策の常套手段である
「認証後にSessionIDを再発行」をどのようにすべきか考えていましたが、上記リンクの下のほうにある
「シンプルにする」が解の一つかと思います。
「新しいSessionを使う」という観点でなく、「誰かに用意されたSessionIDを使わない」という逆の観点での実装ですね。
Dim userData As User = (From d In db.Users Where d.username = userAccount).FirstOrDefault
1件もしくは0件のデータを明示的に取得する場合、FirstOrDefaultを使用すれば
単独レコードが取得できます。もしもレコードがなければ、Nothingが格納されます。
Dim viewData As ViewDataDictionary = New ViewDataDictionary() viewData.Add("Param1", "param1") viewData.Add("Param2", "param2") Html.Partial("parts.vbhtml", viewData)
引数にViewDataDictionaryインスタンスを渡せば、部分ビュー内で
viewData("Param1")
とすれば参照することが可能です。
と、上記の書き方だと問題が発生します。。。
Dim tmpViewData As ViewDataDictionary = New ViewDataDictionary() tmpViewData.Add("Param1", "param1") tmpViewData.Add("Param2", "param2") ViewData.Add("Param0", "param0") Html.Partial("parts.vbhtml", tmpViewData)
上記はコントローラとビューの仲介に使われる「ViewData」も併用している例です。
この場合、Html.Partialが呼ばれると子要素内でViewDataの値はParam1、Param2となります。
子要素内のViewDataが引数のtmpViewDataで上書きされるんですね。
なお、親に戻った場合はViewDataはparam0となります。
Dim tmpViewData As New ViewDataDictionary(ViewData) tmpViewData.Add("Param1", "param1") tmpViewData.Add("Param2", "param2") Html.Partial("parts.vbhtml", tmpViewData)
パラメータを渡す際は、あくまでViewDataへ追加という形で渡す必要があります。
SQL Azureを指定する場合です。
(参考).NET Framework 4 メンバーシップでSQLServerを指定する際の設定
SQL AzureにASP.NETメンバーシップ、ロールデータベース構成
aspnet_regsqlazure.exe -s SERVER.database.windows.net –d DATABASE -u USER -p PASS -a all
※「Cannot grant, deny, or revoke permissions to sa, dbo, entity owner, information_schema, sys, or yourself.」
とエラーが出ても無視して良いみたいです。
専用のツールを使用することで、SQLServerと同様の初期設定を行うことができます。
VisualStudio2010でWebプロジェクトを作成すると、ログイン機能などが実装された
テンプレートがデフォルトで読み込まれます。
このユーザー管理機能はメンバーシップフレームワークを使用するのですが、
初期設定はデータはすべてローカルDBファイルに格納されます。
外部のSQLServerを指定することも可能ですが、その場合はメンバーシップフレームワークに
関連するテーブル等を作成することが必要です。
C:WindowsMicrosoft.NETFrameworkv4.0.30319aspnet_regsql.exe
このウィザードを実行することで、必要な初期設定が完了します。
Razorのカスタムヘルパー内でActionLinkを使用する場合、
@helper CustomActionLink(label As String, action As String, controller As String) @Html.ActionLink(label, action, controller) End Helper
上記のように@Helper内でHtml.ActionLinkを使用するとエラーとなります。
Helperのインスタンスが
System.Web.WebPages.Html.HtmlHelper
となっているためです。System.Web.Mvc.HtmlHelperを使うため、
@helper CustomActionLink(html As System.Web.Mvc.HtmlHelper, label As String, action As String, controller As String) @html.ActionLink(label, action, controller) End Helper
と明示的にHtmlヘルパーのインスタンスを渡すようにして回避しました。
連載:Entity Framework 4.1入門 第2回 EF 4.1の規約とデータベースの初期化方法
まだまだまとまった記事がないので貴重です。
既存のDBに合わせてコードファーストを使う場合に必要なテクニックになりますので
最初に読んでおいたほうがいいでしょう。
メソッドにNonActionAttribute属性を付与することで、外部公開されなくなる
<NonAction()> Function Index() As ViewResult ... End Function
コードファーストで、定義したエンティティクラスはcontextクラスにプロパティとして登録することになります。
便利な機能なのですが、ここでふと疑問が。contextはどういう単位で作成すべきなのか?
エンティティ単位にcontextを作成すべきなのか、一つのcontextに詰め込むべきなのか。
はたまた使用するエンティティの種類によって適宜切り替えるべきなのか。
(可能であれば一つのcontextに詰め込んだほうが作りがすっきりする)
想像するに、内部ではエンティティクラスを参照してSQLを組み立てたりしているので、登録されるエンティティが
多くなると、きっとパフォーマンスが悪くなっていくのでしょう。
しかし、いろいろなサンプルを参照しても、一つのcontextにすべてのエンティティをまとめているものばかり。
いったいどれだけのエンティティを詰め込んだらパフォーマンス的に支障が出るのか調べてみました。
調査
エンティティは下記の3クラスを1組とします。各クラスでリレーションが張ってあります。
Public Class SizeTest1Item1 <Key()> <DatabaseGenerated(DatabaseGeneratedOption.Identity)> Public Property id As Integer Public Property option1_id As Integer Public Property data1 As String <ForeignKey("option1_id")> Public Overridable Property Item() As SizeTest1Item2 End Class Public Class SizeTest1Item2 <Key()> <DatabaseGenerated(DatabaseGeneratedOption.Identity)> Public Property id As Integer Public Property option2_id As Integer Public Property data2 As String <ForeignKey("option2_id")> Public Overridable Property Item2() As SizeTest1Item3 End Class Public Class SizeTest1Item3 <Key()> <DatabaseGenerated(DatabaseGeneratedOption.Identity)> Public Property id As Integer Public Property data3 As String End Class
上記の組を名前を変えつつ1、10、20、…、300、500と増やし、contextに登録して計測します。
計測は下記の2つで計測します。
1.contextクラスのインスタンス生成
2.データ取得
2ではincludeを使用して、遅延ロードが行われないよう(joinしたSQLが発行されるよう)にしています。
Dim sw As New Stopwatch sw.Start() Dim db As SizeTestContext = New SizeTestContext sw.Stop() Dim sec As Double = sw.ElapsedTicks / Stopwatch.Frequency System.Diagnostics.Debug.WriteLine("time " + sec.ToString()) Dim id As Integer = DateTime.Now.Ticks Mod 10000 sw.Reset() sw.Start() Dim data3 = (From a In db.SizeTest1Item1s.Include("Item").Include("Item.Item2") Where a.id > id).ToList() sw.Stop() sec = sw.ElapsedTicks / Stopwatch.Frequency System.Diagnostics.Debug.WriteLine("sql time " + sec.ToString())
結果
組数 | エンティティ数 | インスタンス生成(s) | データ取得(s) | ||
初回 | 2回目以降 | 初回 | 2回目以降 | ||
1 | 3 | 0.0040139000 | 0.00002185 | 0.380862 | 0.00700525 |
10 | 30 | 0.0367546 | 0.000054625 | 0.6435081 | 0.0074485 |
20 | 60 | 0.085165 | 0.000098675 | 0.9136356 | 0.010602125 |
30 | 90 | 0.1359965 | 0.000155375 | 1.4051476 | 0.00925665 |
50 | 150 | 0.2135294 | 0.000204075 | 2.5750359 | 0.0142086 |
100 | 300 | 0.4460645 | 0.000499725 | 9.8501423 | 0.02892025 |
200 | 600 | 0.921872 | 0.001003225 | 53.739042 | 0.064910225 |
300 | 900 | 1.3339758 | 0.00120595 | 168.6336884 | 0.1096848 |
500 | 1500 | 2.2109845 | 0.0026371 | 718.1666749 | 0.3085771 |
組数として30(エンティティ数90)くらいが境目でしょうか。
これを過ぎると急激にパフォーマンスが悪化します。
逆に言えばエンティティ数90程度のシステムであれば、一つのcontextにまとめてしまっても
問題ないとも言えます。
このあたりは、システムの規模に合わせて要検討というところで。