私的なメモなので、あまり信用しないでください。 実際の動きは自分で確認してください。 基本的に、VS(VB)は2003、.NET Frameworkは1.1、IEは6が対象となっています。 それ以外のバージョンの話題も含まれるかもしれませんが、まあ気にしないでください。 メモというより独り言になっている部分もありますが、今更編集しなおすのも辛いので軽くスルーしてください。 また、同じような内容が繰り返し記述されている可能性もありますが、まとめる気力がないのでスルーしてください。
工事中・・・
現在ナッシングです。
ASP.NET(正確にはWebアプリだけど)で、エラー発生時のユーザーへのレポート方法としてラベルにメッセージを表示させる方法があるが、 HTMLとして画面のどこかにメッセージを表示させる以外の方法として、RegisterStartupScriptによるalertを使った方法もある。 但し、動作環境は限定される(JavaScriptが有効になってないと駄目)し、 他にRegisterStartupScriptを使用している処理もあるかもしれないので、その辺りとの干渉も考えなくてはならないと思う。
FrontPage Server Extensionsがインストールされているか確認する・・・これってほんとか? 既定だとアクセス方法はファイル共有のはず。 もしかして設定しているアクセス方法に関係なくFPSEのインストールが必要??
IISにASP.NETのISAPI設定が行われていない可能性があるので、 %system%\Microsoft.NET\Framework\[対象バージョン] フォルダに存在するaspnet_regiis.exeを-iの引数で実行してみる。
ちなみに、VS.NETをインストールする時にIISが既にインストールされていれば自動で上記のISAPIマッピングも行ってくれるが、 VS.NETをインストールした後でIISのインストールを行った場合は自動でマッピングは行われないので、 上記の手順で設定する必要がある。
とりあえず、ASP.NET以外(SQL*PlusとかWinフォームとか)ではアクセスできることを確認しておく。 以下はASP.NET(と言うか正確にはIIS Webアプリケーション)限定でアクセスできない時の対応方法。 ORACLE_HOMEのアクセス権限を確認する。 デフォルトでは匿名ユーザー(ASPNET等)はORACLE_HOMEへアクセスできない設定になっている。
単純なフォーマット(日付とか数値とか)の場合は、テンプレートの設定で行える(らしい)が、 ちょっと複雑なフォーマットや条件によって表示する値が変わるような処理は無理。 また、DataBindイベントを処理することによりフォーマットすることもできるが、 このイベントが発生するのはデータグリッドにデータソースの値をバインドした直後なので、 扱えるデータはデータソースの値ではなくてデータグリッドに設定されているhtmlになる。 ので、バインド時のデータグリッドの値の処理の仕方によっては、ここでの対応も無理が出てくる。 一番確実で柔軟性がある方法は、DBから取得したデータソースとグリッドに設定するデータソースを別々に用意する方法。 DBから取得した後、データソースを全てループさせてフォーマット処理したデータソースを作り直す手間はあるが、 処理的にはわかりやすく、確実だと思う。
と言いつつも、 Webアプリケーション開発技術大全ではDataGridコントロールのデータフォーマット式を利用してフォーマットして表示するのが正攻法だと書いてある。 なのでそっちの方が正しいんだろう。
別プロジェクトでは同じセッションは扱えない・・・と思っている人もいるかもしれないが、そんなことはない。 別プロジェクトと言うより、別仮想ディレクトリで同じセッションを扱えない。 Visual StudioでWebアプリを作成すると、プロジェクト=仮想ディレクトリ で作成されるので、 デフォルトでは別プロジェクトのセッションが共有できないだけ。
→Webアプリケーション開発技術大全参照。
Server.Transferとかは遷移先ページを別スレッドで新たに作成し、 現スレッドはスレッドのAbortメソッドで強制的に終了させているため(内部的にね)、ThreadAbortExceptionが発生してしまう。 ・・・MSDN参照。 まあ、ThreadAbortExceptionが発生してしまうと言ってもスレッド自体が強制的に終了するので、 (作りによっては)あまり実害はないかも。 ただ、例えばExceptionをキャッチしてエラーログを吐き出すとかの処理が入っていると、 ページ遷移するたびに(必要のない)エラーログが吐き出されることになるため、 そういうのを考えるとできる限りそういう動作を避けるコーディングをした方が良いとは思う (「MSDN Online = 10 行シリーズ 〜 10 行でズバリ !! Web アプリケーションの画面遷移」 でもServer.Transfer時にThreadAbortExceptionを考慮したコードになっているので、 これはこれで1つの解っぽい)。
ちなみに、ThreadAbortExceptionは一旦キャッチしても自動的に再スローされる特殊な例外なので、その辺りも考慮すること。
ちょっと試してみたところでは、 Server.Executeメソッドはなんか動きがおかしかったので(動作をよく理解していないってのもあるけど) Response.Redirectの引数をFalseを設定するのがうまくいくかな・・・。
Webアプリケーション開発技術大全にもResponse.Redirectを使えと書いてある。
・・・ちょっと心配だったんで調べてみたんだけど、遷移前も遷移後も同じスレッド(ID)で処理されてる・・・。 少なくとも、開発環境で試した分には。 同じスレッドを使いまわしてるからじゃないかなとは思うんだけど、Abortさせても使いまわせるんだっけ・・・? いや、自滅させてもAbortさせてもどっちでもスレッドが終わること自体は変わらないから、使いまわせる・・・のか? でも、スレッドに設定したNameとかの設定も残ったまんま? この辺はもうちょっとスレッドについて突っ込んで調べてみないとわからないかも・・・。 でも単純に考えると、Server.Transferが実行された瞬間、遷移先のページが新しいスレッドで実行開始されて、 現在のスレッドがAbortされると思えるので、 その仮定でいくとServer.Transferでも同じスレッドが使用されているのは納得できない・・・(Response.Redirectだったらまだわかるんだけど)。 謎。っつーか、ほんとにこのスレッド、Abortしてるのかさえ疑わしく思えてくる。まあ、ThreadAbortExceptionは発生してるけど・・・。 それとも、あれか、Server.Transferの動きは、 カレントスレッドがAbortした後(Response_Endした後?)じゃないと遷移先のページが実行されないのか? だとしたらわからないでもないけど・・・。
まあでも、少なくともHttpContextについては思っていた通りの動きをしていた。 Server.Transferでは同じHttpContextが使われて(同じRequest内で処理されていて)、 Response.Redirectでは違うHttpContextが使われていた(異なるRequestで処理されていた)。 でも、新たな謎なのが、Server.TransferってResponse.Endを呼び出すからスレッドがAbortされるはずらしいんだけど、 Response.EndするっていうことはそのRequest(HttpContext)はそこで終わっちゃうって事じゃないのか? これも謎。
いや、そもそもHttpContextってスレッドに固有のデータスロットを使用してるんじゃないの? (Webアプリケーション開発技術大全にそう書いてある) だとすると、スレッドをAbortするって事はデータスロットのデータも破棄されるって事じゃないの??
いやいや、ちょっと待った。 例えばServer.Transferで遷移したとしても、遷移先のページではHttpContext.Handlerを通してHTTP受信元のページインスタンスにアクセスできる。 ってことは、ThreadAbortExceptionしてるからページのインスタンスが破棄されてるってわけじゃ必ずしもないってこと。 スレッドとインスタンスの関係って・・・どうなんだろ? 可能性としては、HttpApplication単位(?HTTP受信単位?)でスレッドが作成されて、 HttpHandlerは全てそのスレッドで処理されてる? Server.Transferで遷移する際も、同じスレッド内で異なるHttpHandlerインスタンスを作成して処理してるだけ?? この仮定が成立するには、どこかの時点でThreadAbortExceptionをキャンセルしている必要がある(と思います)。
蛇足:
ThreadAbortExceptionがスローされたときだけPage_Errorとかが呼ばれないなぁと思ってたら、
やっぱりThreadAbortExceptionだけは特別扱いされてるみたい。
ThreadAbortExceptionが発生した時はSystem.Web.UI.Control::UnloadRecursiveをコールしてて、
その他のException(正確に言うとちょっと違うけど)がスローされるとSystem.Web.UI.Page::HandleErrorをコールしてる、らしい。
モーダルダイアログでポストバックが発生すると、デフォルトの状態では同じダイアログがもう1枚開いてしまい、
正常に動作しない。モーダルダイアログで正常に動作させるには、headタグ内に
<base target="_self">
を記述するか、iframeタグを使用する。
baseタグを指定する方が汎用的かな?
・・・でも、上記の方法でポストバック自体は発生するんだけど、どうやらブラウザのウィンドウごとリフレッシュがかかるっぽい。 ウィンドウ表示時、画面の真中に表示されて、そのウィンドウを別の位置に移動させてからポストバックを発生させると、 ウィンドウが再び初期表示時の位置に表示されてしまう(移動してしまう)。 この現象はbaseを指定したときにのみ発生し、iframeを指定した場合は発生しない。 ので・・・基本的にbaseタグじゃなくてiframeを使用するべきだと思う。 上記動作はちょっと許容できない動き。iframeを使用した場合の問題点は今のところ出ていない。
後、モーダルダイアログはデフォルトでブラウザにキャッシュされてしまうようなので(ホントか? 未調査)、注意。 キャッシュさせたくない場合はサーバー側でResponseオブジェクトとかの設定を行っておく。
モーダルダイアログと言うよりは、ブラウザからURLを直接リクエストさせた場合。 モーダルダイアログの表示はブラウザからモーダルダイアログのURLをリクエストする形になるので結果的にキャッシュの問題が発生する。 モーダルダイアログとかでなくても、リンクやクライアントサイドスクリプトなどでURLをリクエストしているんであれば同じ。
クライアントサイドでチェック処理などを行い、
値に問題なければPostBackさせてサーバーサイドの処理を行いたい場合、
コントロールのonchangeイベントなどにクライアントサイドの関数をバインドさせて(Attributes.Add)、
さらにAutoPostBackもTrueに設定させる。
しかし、クライアントサイドでAutoPostBackをキャンセルする為には
"return 関数()"
などとして戻り値によりそれ以上の処理をしないようにしなくてはならないが、
そうするとAutoPostBackが発生しなくなってしまう(と言うか、次の__doPostBackメソッドが呼ばれない)。
"return"
をとればクライアントサイドの関数が呼ばれた後にPostBackも発生するが、
そうすると常にPostBackが発生してしまう。
クライアントサイドでの処理を行い、
その処理結果によってPostBackさせたいのであれば・・・AutoPostBackプロパティを使ってPostBackさせるのではなく、
該当のクライアントサイド関数内でsubmitさせてPostBackさせる(eventオブジェクトで操作しようとしてみたけど駄目だった)。
・・・と思ってたけど、AutoPostBackプロパティでPostBackさせようとしていると言う事は、
元々デフォルトアクションがないので、
より上位のオブジェクトでイベントをキャッチとかしていない限りは
"return"
をつけてもあまり意味ないかも(その次にバインドしている関数が呼ばれないと言う意味以外では)。
んで、たとえば"if (関数()==true)"
みたいな形でイベントにバインドすれば、結果として吐かれるクライアントサイドスクリプトは
"if (関数()==true)__doPostBack()"
の形になるので、これだったら関数の戻り値によって__doPostBackを呼ぶか呼ばないかを操作できる。
まあでも・・・関数内でsubmitするのもシンプルだとは思う。
ちなみに、ボタンコントロールには上記の動きは該当しない。 ボタンコントロールがクリックされた時のデフォルトのアクションがサブミットであり、 onchangeなどのイベント内でtrueをreturnすればデフォルトアクションが実行される為。
たぶん、最新のソース(画面に表示されているソース)で実行されていない。 実際にはテンポラリフォルダに存在する以前のソース(DLL)が実行されている。 なので、画面上もブレークポイントで停止しているように見えたりするが、 画面に表示されているソースとは無関係(前のソースでデバッグしている)。 とりあえず、確認する事は2点。 本当に最新のソースがコンパイルされているか(コンパイルに失敗していないか)の確認と、 テンポラリファイル(Temporary ASP.NET Files)の削除。 コンパイルに失敗していても、テンポラリファイルが残っていれば無理やり実行することも可能なので、注意。 その場合、昔のソースが使用されるので、意図しない動きになることが多い。
単純にラジオボタンやコンボのselectedやcheckedをtrueにして選択項目を変更しようとすると、おかしな動きになる。 ので、変更前の選択項目のselectedやcheckedをfalseにしてから、該当項目をtrueに設定する。
ASP.NETの実行時に必要となるDLLは、そのWebアプリケーションの物理フォルダの下にあるbinフォルダのDLLというわけではない。
正確には、「実行されている(Web)アプリケーション名のbinフォルダ」に存在するDLLが実行(参照)される。
通常はWebアプリケーションの物理フォルダ名(プロジェクト名)がそのまま(Web)アプリケーション名になるが、
対象となるプロジェクトがあるWebアプリケーションのサブフォルダという位置付けになっていると話が違ってくる。
この場合、対象プロジェクトのbinサブフォルダはIIS(正確にはASP.NETランタイムね)からアクセスされず
(まあ、ここにbinサブフォルダがある事自体どうかと思うけど)、
対象プロジェクトの(Web)アプリケーション名のフォルダのbinサブフォルダのDLLがIISからアクセスされる。
ただし、aspxファイル自体は、対象プロジェクトフォルダのaspxがアクセスされる(当たり前かもしれないけど)。
ここで言っているbinアクセス先というのは、
対象aspxファイルが継承しているクラス(PageディレクティブのInherits属性の値)を参照するときの話。
この時に参照するDLL(クラス)が見つからないとかになると、
「〜アプリケーションでサーバーエラーが発生しました。」とかの解析エラーが表示されることになる。
ここでの読み込みが正常に終了して、初めて
%SYSTEM%Microsoft.NET\Framework\[バージョン]\Temporary ASP.NET Files\[仮想ディレクトリ名]\...\assembly\
の方の処理に入る。
この辺の話については、Webアプリケーション開発技術大全(Vol.3)にも 詳しく載ってる。と言うかそっちの方が詳しく正確なので、そっちを参照。
上述しているDLLの説明と同じく、Web.configなどの設定ファイルについても同様のことが言える。 つまり、単純にプロジェクトフォルダ直下にあるWeb.configが参照されるとは限らないということ。 参照されるWeb.configは、「実行されている(Web)アプリケーション名フォルダ」であって、 必ずしも「(プロジェクトの)物理フォルダ」に存在する設定ファイルが参照されるというわけではない。 仮想ディレクトリとして各サブプロジェクトを管理しているのではなく、 セッション情報共有などの問題で各サブプロジェクトを単純なサブフォルダとして扱っている場合は注意。
ASP.NETの分離コードモデルでは、分離コードのコンパイルは事前コンパイルを行い(binに配置)、 ASPXページは初めて要求されたときに実行時コンパイルされる(一時ディレクトリに格納)。
この辺の話については、Webアプリケーション開発技術大全にも詳しく載ってる。
ASP.NETのポストバック処理はその性質上、クライアントサイドでのコントロールのプロパティ値変更と相性が悪い (もちろん、POSTされた時に自動で値が送信されるプロパティ(テキストボックスの入力値とか)は問題ない)。 その辺を考慮しないで、 「クライアントサイドで処理した方がユーザビリティが良くなるから」程度の考えでクライアントサイドでの処理をゴリゴリ書いちゃうと、 ポストバックが発生した際に破綻する可能性が高い。 クライアントサイドでコントロールのプロパティを操作するような処理をしたい場合は、 実際にアプリケーション全体の動きとして採用する前に入念な動作確認をした方が良い。 また、コードで共通化できるのであれば共通化するに越したことはない。 カスタムコントロール(ユーザーコントロール)として共通化できるんであれば、 そっちの方が使うほうは楽かも(作るのに相応の労力が必要になるけど・・・)。
データバインディングではHTMLエンコードは一切行われないらしい。
→たぶん、違う。「データバインディング」と言うのが具体的に何を指すのかが分からないが(DataGrid限定で言っているのかな)、 HTMLエンコードが行われるか行われないかは使用するコントロールによる。後述。
基本的にはサニタイズしなくてはいけないが、使用するコントロールによってはhtmlレンダリング時に自動でサニタイズを行う。 例えば、LabelコントロールはTextプロパティの値がそのままhtmlに出力されるので、自前でサニタイズが必要。 逆に、TextBoxコントロールはTextプロパティの値が自動でサニタイズされて出力されるので、自前でサニタイズしてはいけない。 サニタイズしなくても良いのではなく、してはいけない。 もししてしまうと2回サニタイズされた値が表示されてしまう (元の値が"&"だとすると、"&"が画面のテキストボックスに表示される (htmlの実際のvalue値は"&amp;"))。
でも、これってどのコントロールだと自動でサニタイズされるのかが・・・良くわからない。 ドキュメント見ても特にその辺の記述が見つけられなかったし・・・。 実際に使う前に自動でサニタイズされるか試すしかないか? ・・・と思ったけど、LabelやDataGridの説明には表示する前にHTMLエンコードされないから注意しろと書いてあった・・・。 どうやら自動でHTMLエンコードされないコントロールには注意書きが書いてあるらしい・・・たぶん。
でも上記ページの説明だと、「データ連結 Web コントロールでは、出力をエンコードしません。 出力をエンコードする唯一のコントロールは TextBox コントロールで、 しかもその TextMode プロパティが MultiLine に設定されている場合です。」 って書いてあるんだよなぁ・・・。 TextModeプロパティがSingleLineでもエンコードされるんだけど・・・。
サニタイズによる具体的な防御方法については以下のページを参照。 サニタイズした後に、クライアントでhtmlタグを有効にしたい場合についても説明されてる。
また、あんまり関係ないがサードパーティーのコントロールだとHtmlEncodeをするかしないかのプロパティがあったりする(グレープシティのとか)。
また、この辺りの事はWebアプリケーション開発技術大全にも詳しく載っていて、 ある程度以上の規模のWebアプリケーションだったら自動でHTMLエンコードを行うカスタムのLabelコントロールを作成したほうが良いと書いてあった。 まあ、そうかもしれない。
個人的にはLabelコントロールの自動HTMLエンコード対応ぐらいはLabelコントロールから派生させたカスタムコントロールで対応した方が良いと思う。 そのぐらいのカスタムコントロールであれば作るって言っても(いちいち個別に対応するよりは)ほとんど労力いらないだろうし。 まあ、まだ一度もそういう対応したことないんであれだけど。
ASP.NETではクライアントから送信されたコントロールの値を元にサーバーサイドで自動で設定するので、 画面再表示時にPOST前の状態が自動で回復される(POSTで送信された情報に関して)。 但し、disabledがtrueに設定されたコントロールの値はPOST時にサーバーに送信されない(HTMLの仕様)。 ので、クライアントサイドでdisalbedをfalseからtrueに変更すると、ASP.NETの制御下から外れてしまう。 この状態でポストバックが発生すると該当コントロールの値が送信されてきていないので、 ASP.NETによる状態回復を行うことができない。 ASP.NETではページオブジェクトが作成された際、デザイン画面の値(aspxコードでの値)で各コントロールのプロパティが初期化されるので、 結果としてデザイン画面で設定されている値が画面再表示時に表示されることになる (自動状態回復が行えていれば、上書きされているので問題ない)。 よって、クライアントサイドでdisabledを操作するコントロールに関してはポストバック時に明示的に状態回復処理などを行う必要がある (状態回復処理をASP.NETに任せることができない)。 またこの場合、disabledのコントロールに設定されている値はサーバーサイドでは取得できないので、 設定されていた値を回復したいとかであれば、 隠しコントロール経由とかで設定されていた値をサーバーサイドに送る仕組みも用意しないといけない (あらゆる操作での整合性を保ちつつこの処理を実装するのは、正直非常にめんどくさい・・・)。 もし、コントロールの使用可・不可を操作しつつ状態回復処理もASP.NETに任せたいのであれば、全てASP.NETで操作しなければならない。 つまり、クライアントサイドで発生したイベントをサーバーサイドで受け取り、サーバーサイドで該当コントロールのEnableプロパティを操作する。 EnableプロパティがFalseの場合も該当コントロールのdisabledがtrueになるが、 代わりに該当コントロール用のViewStateに状態回復用の値が格納されるのでポストバック時にもASP.NETが自動で状態を回復してくれる (Traceで該当コントロールのViewStateが増加していることを確認できる)。 まあ、その場合当たり前だけど、クライアントサイドのみで処理する時と比べて余計なラウンドトリップが発生することになる。
また、上記の仕様をふまえた上での応用技として、 全てのPOST前にクライアントサイドで該当コントロールのdisabledをfalseに設定するという技もある。 こうすると、クライアントサイドでdisabledを操作しつつ、サーバーサイドでもコントロールの値を受け取る事ができる。 ・・・けど、POST処理(もちろんポストバックも含む)をハイジャックしないといけないし、 コントロールの表示が一瞬変わるので(disabledがtrueからfalseに変わるので)、 ちょっと微妙な感じ(まあ、その程度の事だったらstyleとかの操作も行えばどうにでもなるような気がしないでもないけど)。
この辺の処理に関しては、アプリケーション全体の設計を行うときに決めておく必要がある。 いちばん簡単なのは、クライアントサイドでdisabledを操作しない事。まあ、それが許されるのであればだけど。
もしdisabledをクライアントサイドで操作する(且つポストバック後も状態を回復させる)ような仕様にするのであれば、 個人的な意見を言うとできればカスタムコントロールを作成して処理させたいところ。 まあ、カスタムコントロールの作成はそれはそれで結構大変なんだけど、 対応する画面数が多くなればなるほどカスタムコントロールで対応するべきかなとは思う。
ASP.NETのイベントは、Change系イベント→Click系イベントの順にコールされるが、 Change系イベントについては順不同でコールされるので、順不同で呼ばれても正常に処理できるようなコーディングを行わなければならない。 また、コール予定のイベントに関しては普通に(?)コーディングをしている限り全て呼ばれるはずなので、 それを念頭においてコーディングしないと、 他のイベントでチェックにひっかかったので本来ならば実行しなくて良いイベントまで実行してしまったりするので注意する (残りのイベントは全てキャンセルされるとか勝手に思ってると、痛い目にあう)。
プロセス 1-* AppDomain 1-* Thread
[ASP.NET]
プロセス[aspnet_wp.exe]
1-* AppDomain[Webアプリケーション毎]
1-1 HttpRuntime(パイプラインのエントリポイント)
1-* HttpApplication(=Global.asax)(HTTP要求毎)
1-1 HttpHandler(AppDomainの下か?)(パイプラインのエンドポイント)
1-* HttpContext(HTTP要求毎?)
スレッドはどこに入るの?
HttpApplication ->(継承)Global.asax(=アプリケーションクラス)
『初めて要求が送信されると、HttpApplication のインスタンス プールが作成され、
Application_Start イベントが発生します。
これらの HttpApplication インスタンスは、最後のインスタンスが終了し、
Application_End イベントが発生するまで、これ以降の要求をすべて処理します。 』
(Microsoft ASP.NET クイック スタート チュートリアル アプリケーションの概要 より)
『どの Web アプリケーション ドメインにも、HttpRuntime のインスタンスが 1 つずつあります。』
(セキュリティ保護された ASP.NET アプリケーションの構築 : 動作のしくみ より)
『HTTP パイプラインは HttpRuntime クラスの新しいインスタンスを作成し、
次にその ProcessRequest メソッドを呼び出すことでアクティブになります。
前述のように、ASP.NET では単一のワーカー プロセスが常に実行されており (Web ガーデン モデルが有効になっていることを除く)、
個別の AppDomain で Web アプリケーションをすべて管理します。
各 AppDomain には HttpRuntime クラスの独自のインスタンスがあります。
これはパイプラインのエントリ ポイントになります。
HttpRuntime オブジェクトは、要求の実行を支援するいくつかの内部オブジェクトを初期化します。
ヘルパ オブジェクトは、キャッシュ マネージャ (Cache オブジェクト) と、
アプリケーションを形成するソース ファイル内の変化を検出するために使用する内部ファイル システム モニタを含んでいます。
HttpRuntime は要求のコンテキストを作成し、要求に固有の HTTP 情報を挿入します。
コンテキストは、HttpContext クラスのインスタンスで表されます。』
(ASP.NET HTTP ランタイム より)
★関係が良くわかってないオブジェクト HttpContext(Context) HttpRequest(Request) HttpResponse(Response) HttpServerUtility(Server) HttpSessionState(Session)
参考サイト
(図がわかり易い・・・けど、スレッドプールってアプリケーションドメイン毎に用意されるんじゃないの? 内容読んでないから詳細不明。) (・・・と思ったけど、「スレッド プーリング」を見ると「各プロセスには ThreadPool オブジェクトが 1 つだけあります。」とある。 AppDomainの中だからマネージなんじゃないの? AppDomainの外でもマネージオブジェクトが使えるの?? もうわけわかめ。
(・・・と思ったけど、Threadクラスって、CLRが作ったスレッドもランタイム外で作られたスレッドも管理してる??? わけわかめ)
(・・・解決(?)。アプリケーションドメインとスレッドの関係を勘違いしてた。 CLR及びマネージコード自体は確かに各アプリケーションドメインで実行されるけど、 スレッドに関してはあくまでもOSのスレッドを利用している。 これに関してCLRやマネージは関係ない。スレッドはアプリケーションドメインを超えて使用される。 アプリケーションドメイン毎にスレッドが作成されるわけではない。)
(詳しい。けど、内容の一部にはちょっと疑問がある。 Applicaton_OnStartはIExecutionStepの前に発生すると書いてあるけど、 呼び出し履歴で確認するとglobal.asaxのApplication_StartイベントはIExecutionStepの処理で呼ばれている。 ・・・でも、何か勘違いしてるかもしれない)
(英語なのでよく分からないけど、何となく知りたそうな事が載っている気がする)
(ちょっと違うけどこれも結構詳しい)
(凄い詳しい。けど英語なのでよく分からない)
処理される順番はVS.NETで適切な場所にブレークを張り、 呼び出し履歴を見ることでも確認することができる(非ユーザーコードも表示する必要がある)。
特定のフォルダのASP.NETファイル(aspxとか)がどのWebアプリケーションに属しているかは、 ISMで該当フォルダのプロパティを開くと「アプリケーション名」に表示されている。 たぶん、通常のフォルダの場合は最も近い親Webアプリケーションが表示されている(そこに属している)。
1. リクエストされたaspxファイルのISM上の仮想フォルダから上の階層に向かってweb.configファイルを検索しつつ処理 (たぶん[既定のWebサイト]まで上った後、machine.configを適用して終わりだと思う。未検証)。 authenticationセクションとかsessionStateセクションとかフォルダの位置によっては使えないセクションもあるみたいなので注意。
2. リクエストされたaspxファイルが継承しているクラス
(PageディレクティブのInherits属性。ちなみにCodebehind属性はVS.NETのWebフォームデザイナが使用しているだけなので実行時は関係ない)を、
属しているWebアプリケーションのアセンブリ(Webアプリケーションフォルダ直下のbinフォルダ(固定)内に存在するdll)から検索し、
aspxファイルに継承させてコンパイルし、(一時的な)dllをASP.NETのTemporaryフォルダに作成する。
3. 2で作成された一時dllが実行されてレスポンスを出力する。
開発技術大全Vol.2およびVol.3も参照。
web.configファイルはISM上の仮想ディレクトリ構造に沿った形で適用される。 適用される順番はリクエストされたaspxファイルが存在するディレクトリからスタートし、 ISM上の仮想ディレクトリ構造を上に昇りながら順にweb.configを検索、 適用していく(物理ディレクトリ構造は影響しないことに注意)。 最終的には[既定のWebサイト]のweb.configを適用した後、machine.configを適用してconfig適用処理が終了する。 実際に適用される値は、継承の動作と同じく下位のconfig設定が優先して適用(オーバーライド)される (aspxファイルと同一フォルダに存在するweb.configの設定が一番強く、machine.configが一番弱い)。
configの適用に限ってのみ、Webアプリケーションの構造とは適用ルールが異なる点に非常に注意。 (よって、別Webアプリ間でSessionの共有はできないが、configの設定は配置によっては共有される)
configファイル設定変更時は影響するWebアプリケーションが強制的にリスタートする可能性があるので、注意すること。 特に(machine.configはともかく) [既定のWebサイト] のweb.configは [既定のWebサイト] 以下に存在する全てのWebアプリケーションが対象となるので、注意。 と言うか、少なくとも [既定のWebサイト] にweb.configを配置することは通常ないかな・・・。
同じように見えるconfigファイルだが配置される場所によって3種類に分けられる。
構成設定はそれぞれマシンレベルでしか行えないもの、 アプリケーションレベルでしか行えないもの、 ディレクトリレベルでも行えるものがある。 ほとんどの項目はアプリケーションレベルでしか行えないため、 メンテナンスや管理の事も考えると問題がなければアプリケーションレベルのweb.config一本で行くのがわかり易いと思う。 また、更に一歩進めて下位configファイルによる不正なオーバーライドを防ぐ為にlocationタグとallowOverride属性を指定して (ロックダウンして)おくと、間違って下位フォルダにweb.configを配置してしまった時の防止策となる。
この辺の設定はドキュメントでちゃんと管理しておかないとわけがわからなくなるので、ドキュメントにまとめておく。 設定管理シートの例は開発技術大全Vol.3の5.3.1を参照。
appSettingsの設定に関してのみ、user.configによるオーバーライドが可能。 よって、各開発者によって設定値を変えたい場合は(VSSを使用していると問題があるので)web.configの値を直接いじるのではなく、 user.configをローカルに用意してそっちで設定値をオーバーライドする(もちろんVSSでは非管理)。
Webアプリケーション直下のglobal.asax? リクエストされたaspxと同一ディレクトリに存在するglobal.asax? ヘルプ読んだ限りだとWebアプリケーション直下のglobal.asax。
でもって、コードビハインドのglobal.asax.vbについてはコンパイル済みのはずなので、 複数プロジェクトを一つのWebアプリにまとめている場合・・・どうなっちゃうの? 単純にコンパイルしてると名前空間が違うglobalクラスがプロジェクト分(dll分)できちゃってるはず。 でもって、どれを使うかはglobal.asaxのInherits属性で指定されているglobalクラスのはず。
この辺りの仕組みはaspxと変わらないが、global.asaxにUI要素はない為、 結果としてglobal.asaxファイルにはApplicationディレクティブだけが存在し、 Inherits属性で継承するクラスを指定するだけになっている(コードビハインドを使用する場合)。 ・・・と思ったけど、Applicationディレクティブだけでもないみたい。 objectタグによりオブジェクトを定義することも可能らしい。
ちなみにglobal.asaxはaspxと違い直接リクエストされるわけではないが、 Webアプリケーションへの初回リクエスト時にASP.NETによって自動的にコンパイルされて、以後、 HttpApplicationインスタンスプールで使用される。
global.asaxの動きが場合によってかなり不安定(と言うか内部動作がわからない)。 各プロジェクトごとに用意したglobal.asax.vbをビルドしてその複数のプロジェクトを一つのWebアプリとして扱うと言うのがまずそもそも間違っているとは思うが、 そういう構成にした場合、静的メンバに関してはそれぞれのプロジェクトのアセンブリに含まれているが、 パイプラインのイベント処理はglobal.asaxで指定されたglobalクラスに登録されたイベントが実行されているっぽい。 aspxでglobalの変数を参照した場合は自分のプロジェクトのglobalの変数を参照している。 この辺りに関してはもうちょっと突っ込んで調べてみないとよくわからない。 まあどっちにしろ、globalは一箇所でのみ管理すべき。
TextBoxコントロールのAutoPostBackをTrueにすると、値が変更されてフォーカスが外れたタイミングでポストバックが走る。 この際、ポストバックされる状態はフォーカスが外れた瞬間の状態なので、もしマウスでボタンをクリックしていたり、 チェックボックスをクリック(チェック)していたとしてもそのアクションは(結果的に)無視される。 ボタンのクリックイベントは起こらないし、変更されたチェック状態も変更前の状態で再描画される(結果的に、変更されない)。
ただ、この挙動はブラウザによって違っても全然おかしくないと 思う(未確認)。上の挙動はIE6の動き。
Server.Transferで画面遷移を行うと、遷移元の画面で存在していたQueryStringが遷移先の画面にも自動的に引き継がれるので注意。 Server.TransferとPostBackで行っている間はずっとQueryStringが自動で引き回されている。
どのようなデータを共有データとしてキャッシュしておくかは、慎重に考える必要がある。 区分値など、DBに値を持っていない情報に関しては共有データとしてキャッシュしておいて問題ない。 が、DBのデータを共有データとしてキャッシュする場合、基本的にはDBのデータが変更されただけでは共有データに反映されない為、 その辺りを考慮する必要がある。 DBのデータを変更したらWebアプリケーションをリスタートさせて最新データを取り直すなどの運用方法で良いのであれば問題ない。 例えば、都道府県の情報とかはそういう扱いでも問題ないと思う。 また、DBではなくファイルにデータを保持しているのであれば、System.Web.Caching.Cacheクラスを利用できる。 このクラスを利用すれば、ファイル変更や経過時間などによるデータ失効を行うことができる。
要は、データ変更時のシステムへの反映のタイミングとその方法を充分に検討しなくてはならない。
この辺りについては開発技術者大全Vol.3が詳しい。
縦(横も同様)にスクロールする画面でのポストバックは非常にうざい。 何がうざいって、画面の位置が初期化されるのがうざい。 スクロールが発生しない画面でのポストバックは(結果的には)画面がちらつく程度の動きで済むが、 スクロールが発生する画面だとちらつくどころの騒ぎではない(まあ実際やってみればそのうざさが分かる)。 スクロールが発生する画面ではポストバックさせるような設計にするべきではない。
HTMLソースのダウンロードサイズが大きい場合、 ブラウザに全てのソースをダウンロードしきる前にボタンを押下するなどの操作が可能だが、 ソースをダウンロードしきらないうちにボタン押下した時って、実際にサブミットが走るのはHTMLソースがダウンロードしきった後? それとも、ダウンロードしきる前にサブミットされるのか? でも、特にASP.NETの場合はビューステートの情報がHTMLのソースに格納されるので、 そういうデータがサブミットで送られてこない状態が発生するとASP.NETはまともに動かないのでは? その事を考えると、ボタンの操作はソースのダウンロード中に行えても、サブミット処理(データ送信処理)はダウンロード後に行われるのか?
ブラウザからURL指定された場合、ブラウザのキャッシュの関係でPage_Loadが走らない可能性がある。 ASP.NETはキャッシュされたらまずい気がする。 後、Server.Transferは良いけど、Response.Redirectってキャッシュは大丈夫なの?
ASP.NETでのセッションタイムアウト時間の設定は、web.configで行う。ISMでの設定は関係ないので注意。 ・・・嘘。ISMでの設定も関係してくるらしい・・・けど、どういう優先順位で適用されるのかはちょっと未調査。
・・・以下の優先順位らしい(下に行くほど優先度が高い)
global.asaxで行う処理は、色々な可能性を想定して書く必要がある。 例えば、通常AcquireRequestStateイベントまでパイプラインが進んでいれば、Sessionオブジェクトはインスタンスができている。 が、場合によってはできていない可能性もある。 例えば、ある拡張子は別のHttpHandlerで処理するような場合、 そのHttpHandler内の処理によってはSessionオブジェクトのインスタンスができているとは必ずしも限らない (例えばActiveReportsのHttpHandlerが使用されると上記の状態になる)。
絶対にカスタムのHttpHandlerが使われないと言うのであればそこまで意識する必要もないかもしれないが、 通常は意識するべき。その時は使わなくても、後から使われるかもしれない。
そんな感じなので、例えば上述したようにAcquireRequestStateイベントでSessionオブジェクトを参照するような場合は、 インスタンスが存在することを前提に書くのではなく、存在の確認を行った後に参照を行うようにする。
サードパーティ製のアセンブリ(コントロール)を使用する場合、参照設定への手動追加はなるべくやらないほうが良い。 と言うか、通常はコントロールを貼り付けたタイミングで自動で参照設定に適切なアセンブリが追加されるので関係ないのだが、 他のプロジェクトのファイルをコピーとかした場合、 (ソース)ファイルではアセンブリを使用しているがプロジェクトの参照設定には追加されていないと言う状況が起こり得る。 こういう場合、適切な参照設定を手動で追加しても良いが、 それよりはダミーのフォームを一時的に作成するなりしてそこに使用するコントロールを一度貼り付け、 参照設定が追加された後に不用になったフォームを削除してしまえば考えることがあまりないので楽。 既存のフォームに追加しても同じと言えば同じだが、 その場合HTMLソースに不要なタグプレフィックスが追加される可能性があるので、 そういうのを考慮するとまあダミーフォーム丸ごと削除の方が楽だろう。
テンプレートとして別プロジェクトからaspxファイルをコピーしてきて使用する場合名称の変更が必要になるが、 この変更作業は手動でやるよりVSのプロパティの変更で行った方が楽。 VSで変更すればaspxファイル名変更により影響を受ける箇所も自動で全て変更される。 aspx, aspx.vb, aspx.resxファイルとかCodeBehind設定とか。
それ以外のソースコード内の置換は、プロジェクト内 ファイルの一括置換を行えば楽。
ASP.NETで画面表示時にモーダルダイアログを表示する場合、 RegisterStartupScriptにより登録したスクリプトから表示させてもいいが、 その場合はモーダルダイアログ親画面はHTMLの描画処理がブロックされる事に注意する。 少なくとも処理をブロックする処理(モーダルダイアログとかメッセージボックス表示など)を画面表示時に行う場合は、 RegisterStartupScriptではなくwindow_onloadで行った方がベター。
スクロールとかの話は抜きにして、ポストバックはできる限りさせるべきではない。 ポストバックを多用するような画面は、設計と言うかWEBアプリと言うところから疑ってみるべき。 ビューステート情報が大量+ポストバックしまくりの画面は、使う側からしてみれば悪夢に近い。 また、基本的にユーザーにはポストバックするかどうかの判断がつかないため、 予期せぬタイミングで勝手にポストバックされるとそれだけでいらいらしてしまう。
一概には言えないが、ASP.NETの場合は検索結果を丸ごとビューステートに保存するのは避けたほうが良いと思う。 何かの結果一覧データとかに関しては特にそう。 検索結果に上限を設けていない場合、ビューステートのデータサイズも上限がないのと同じ事になる。 グリッド系のコントロールでページングを使用している場合、画面に表示される件数が10件とかだとしても、 ビューステートには全件(極端な話、数万件とか)保持している可能性があることを考慮しなければならない。 場合にもよるが、毎回DBにクエリーを投げた方がまだ効率が良い気がする。
たぶん、Transfer先のaspxを処理している最中に、何らかのエラーが発生している。 コードがおかしい場合もあるし、HTMLがおかしい場合もある(どちらかと言うとHTML(aspx)がおかしい時が多い気がする)。 ちなみにHTMLがおかしいと言っても、当たり前だが「ASP.NETが処理できない」と言う意味での「おかしい」。 デザイン画面で正常に表示されているかどうかはこの場合特に関係ない。 例えば、ASP.NETのコントロール(タグ)が何らかの拍子にformタグの外に出てしまっていたとか(何だかんだ言って結構ある)、 開発環境ではインストールされていた使用アセンブリがエラー環境だとインストールされていないとか。 このままだと原因の特定が難しいので、とりあえずServer.Transferではなく、Response.Redirectに切り替える。 Response.Redirectだと単発のリクエストになるので、具体的なエラーがでてくるはず。
基本的に、aspxページはクライアントにキャッシュさせない方が変な問題が発生することを考えなくて済む。 クライアントにキャッシュされてると、Page_Loadイベントが発生しなかったりセッション周りの挙動がおかしくなる可能性がある。 基本的にSubmit以外の方法で表示する画面は全てキャッシュの影響を受けると思われる。
DropDownListのAutoPostBackを有効にするのは、色々な意味であまり良くないかも。 DropDownListはマウスホイールで気軽に項目を変更できてしまうのと、 DropDownListのAutoPostBack中にDropDownListをマウスでクリックすると、画面が白くなって何も出来なくなってしまう場合があった。
ASP.NETのラジオボタンはGroupNameの設定によりグループ化することができるが、 グループ化した場合はコントロールのHTMLソース上の出現順に注意する。 グループ化したラジオボタンはキーボードの矢印キーにより選択項目を変更することができるが、 この時に「次のコントロール」等の要素を決める要因は、HTMLソース上の出現順となる。 なので、例えば下キーを押したのに上のコントロールにフォーカスが移動する場合は、 HTMLソース上の出現順が反対になっていると思われる。 手打ちでHTMLソースを書いていればこの辺りがおかしくなる事は通常あまりあり得ないが、 VS.NETのデザイナを使用しているとマウスによるコントロール配置になるので、 特にpageLayoutをGridLayoutで使用している(絶対位置配置)場合は実際に生成されるHTMLソースはタグの記述順がめちゃくちゃになるので、 注意する必要がある。