ASP.NET の成功の理由の 1 つは、Web 開発者の参入障壁を下げたことです。 ASP.NET コードを作成するためにコンピューター サイエンスの博士号を持っている必要はありません。私が職場で会う ASP.NET 開発者の多くは独学で、C# や Visual Basic® を作成する前に Microsoft® Excel® スプレッドシートを作成していました。現在、彼らは Web アプリケーションを作成していますが、全体として、彼らの仕事は賞賛に値します。
しかし、権限には責任が伴い、経験豊富な ASP.NET 開発者でも間違いを犯す可能性があります。長年 ASP.NET プロジェクトに関するコンサルティングを行ってきた結果、特定のエラーが特に再発する欠陥につながる可能性が高いことがわかりました。これらのエラーの中には、パフォーマンスに影響を与えるものもあります。その他のエラーにより、スケーラビリティが阻害される可能性があります。一部のバグは、開発チームがバグや予期しない動作を追跡する貴重な時間を費やす可能性もあります。
ここでは、ASP.NET 運用アプリケーションのリリース中に問題を引き起こす可能性のある 10 の落とし穴と、それらを回避する方法を紹介します。すべての例は、実際の企業で実際の Web アプリケーションを構築した私自身の経験から来ており、場合によっては、ASP.NET 開発チームが開発プロセス中に遭遇した問題のいくつかを説明することでコンテキストを提供しています。
LoadControl と出力キャッシュ ユーザー コントロールを使用しない ASP.NET アプリケーションはほとんどありません。マスター ページが登場する前は、開発者はユーザー コントロールを使用してヘッダーやフッターなどの共通コンテンツを抽出していました。 ASP.NET 2.0 でも、ユーザー コントロールは、コンテンツと動作をカプセル化し、ページ全体から独立してキャッシュ可能性を制御できる領域にページを分割するための効率的な方法を提供します (セグメントと呼ばれる特殊な形式の出力キャッシュ)。 )。
ユーザー コントロールは、宣言的または強制的に読み込むことができます。強制読み込みは、ユーザー コントロールをインスタンス化し、コントロール参照を返す Page.LoadControl に依存します。ユーザー コントロールにカスタム タイプのメンバー (パブリック プロパティなど) が含まれている場合は、参照をキャストして、コードからカスタム メンバーにアクセスできます。図 1 のユーザー コントロールは、BackColor という名前のプロパティを実装しています。次のコードは、ユーザー コントロールを読み込み、BackColor に値を割り当てます。
protected void Page_Load(object sender, EventArgs e){// ユーザー コントロールを読み込み、ページに追加します Control control = LoadControl("~/MyUserControl.ascx") ;PlaceHolder1 .Controls.Add(control);//背景色を設定します ((MyUserControl)control).BackColor = Color.Yellow;}
上記のコードは実際には非常に単純ですが、不注意な開発者が陥りやすい罠です。欠陥を見つけることができますか?
問題が出力キャッシュに関連していると推測した場合、その判断は正しいです。ご覧のとおり、上記のコード例はコンパイルされ、問題なく実行されますが、次のステートメント (完全に正当です) を MyUserControl.ascx に追加しようとすると、
<%@ OutputCache Duration="5" VaryByParam="None" %>
次にこのページを実行すると、InvalidCastException (なんと!) と次のエラー メッセージが表示されます。
「タイプ 'System.Web.UI.PartialCachingControl' のオブジェクトをタイプ 'MyUserControl' にキャストできません。」
したがって、このコードは OutputCache ディレクティブなしでも正常に実行されますが、OutputCache ディレクティブが追加されると失敗します。 ASP.NET はこのように動作することを想定していません。ページ (およびコントロール) は出力キャッシュに依存しない必要があります。それで、これは何を意味するのでしょうか?
問題は、ユーザー コントロールの出力キャッシュが有効になっている場合、LoadControl はコントロール インスタンスへの参照を返さなくなり、代わりに PartialCachingControl インスタンスへの参照を返します。これは、コントロール インスタンスがラップされるかどうかに応じて異なります。コントロールの出力はキャッシュです。したがって、開発者が LoadControl を呼び出してユーザー コントロールを動的にロードし、コントロール固有のメソッドとプロパティにアクセスするためにコントロール参照を変換する場合、コードが存在するかどうかに関係なくコードが実行されるように、その方法に注意を払う必要があります。 OutputCache ディレクティブ。
図 2 は、ユーザー コントロールを動的にロードし、返されたコントロール参照を変換する正しい方法を示しています。その仕組みの概要は次のとおりです。
• ASCX ファイルに OutputCache ディレクティブがない場合、LoadControl は MyUserControl 参照を返します。 Page_Load は参照を MyUserControl に変換し、コントロールの BackColor プロパティを設定します。
• ASCX ファイルに OutputCache ディレクティブが含まれており、コントロールの出力がキャッシュされていない場合、LoadControl は、CachedControl プロパティに基になる MyUserControl への参照が含まれる PartialCachingControl への参照を返します。 Page_Load は PartialCachingControl.CachedControl を MyUserControl に変換し、コントロールの BackColor プロパティを設定します。
• ASCX ファイルに OutputCache ディレクティブが含まれており、コントロールの出力がキャッシュされている場合、LoadControl は、CachedControl プロパティが空である PartialCachingControl への参照を返します。 Page_Load が続行されなくなることに注意してください。コントロールの出力は出力キャッシュから取得されるため、コントロールの BackColor プロパティを設定できません。つまり、プロパティを設定するための MyUserControl がまったくありません。
図 2 のコードは、.ascx ファイルに OutputCache ディレクティブがあるかどうかに関係なく実行されます。少し複雑に見えますが、面倒な間違いを避けることができます。シンプルだからといって、必ずしも保守が容易であるとは限りません。
トップに戻る セッションと出力キャッシュ 出力キャッシュに関して言えば、ASP.NET 1.1 と ASP.NET 2.0 の両方に、Windows Server™ 2003 および IIS 6.0 を実行しているサーバーの出力キャッシュ ページに影響を与える潜在的な問題があります。私は個人的に、この問題が ASP.NET 運用サーバーで 2 回発生するのを確認しましたが、どちらの場合も出力バッファリングをオフにすることで解決されました。後で、出力キャッシュを無効にするよりも良い解決策があることを知りました。私が最初にこの問題に遭遇したときの様子は次のとおりです。
何が起こったのかというと、Web サイト (ここでは Contoso.com と呼びます。小規模な ASP.NET Web 領域でパブリック e コマース アプリケーションを実行しています) から私のチームに連絡があり、「クロススレッド」エラーが発生していると苦情が寄せられました。 Contoso.com Web サイトを使用している顧客は、入力したデータが突然失われ、代わりに別のユーザーに関連するデータが表示されることがよくあります。少し分析した結果、クロススレッドの説明は正確ではなく、「クロスセッション」エラーの方が適切であることがわかりました。 Contoso.com はデータをセッション状態に保存しているようですが、何らかの理由でユーザーが時折ランダムに他のユーザーのセッションに接続します。
私のチーム メンバーの 1 人は、Cookie ヘッダーを含む、各 HTTP リクエストとレスポンスの主要な要素をログに記録する診断ツールを作成しました。次に、このツールを Contoso.com の Web サーバーにインストールし、数日間実行させました。結果は非常に明白です。 ASP.NET は、100,000 要求ごとに約 1 回、セッション ID をまったく新しいセッションに正しく割り当て、そのセッション ID を Set-Cookie ヘッダーで返します。その後、リクエストがすでに有効なセッションに関連付けられており、Cookie 内のセッション ID が正しく送信された場合でも、すぐに隣接する次のリクエストで同じセッション ID (つまり、同じ Set-Cookie ヘッダー) を返します。実際、ASP.NET はユーザーを自分のセッションからランダムに切り替えて、他のセッションに接続します。
私たちは驚き、その理由を調べ始めました。まず Contoso.com のソース コードをチェックしましたが、問題はなかったので安心しました。次に、問題が Web レルムのアプリケーション ホストに関連していないことを確認するために、1 つのサーバーのみを実行したままにし、他のすべてのサーバーをシャットダウンしました。この問題は引き続き発生しますが、ログには、一致する Set-Cookie ヘッダーが 2 つの異なるサーバーから送信されたものではないことが示されているため、これは驚くべきことではありません。 ASP.NET は誤って重複したセッション ID を生成しますが、これは信じられないことです。なぜなら、ASP.NET は、.NET Framework RNGCryptoServiceProvider クラスを使用してこれらの ID を生成し、セッション ID は、同じ ID が 2 回生成されないように (少なくとも次回のセッション ID では) 十分な長さがあるからです。数兆年に二度生成されることはない)。さらに、RNGCryptoServiceProvider が誤って繰り返し乱数を生成したとしても、ASP.NET が不思議なことに有効なセッション ID を新しい ID (一意ではない) に置き換える理由は説明されません。
ある直感から、出力キャッシュを検討してみることにしました。 OutputCacheModule が HTTP 応答をキャッシュするときは、Set-Cookie ヘッダーをキャッシュしないように注意する必要があります。そうしないと、新しいセッション ID を含むキャッシュされた応答によって、キャッシュされた応答のすべての受信者 (およびキャッシュされた応答を生成したリクエストのユーザー) が接続されてしまいます。同じセッションに。ソース コードを確認しました。Contoso.com では両方のページで出力キャッシュが有効になっています。出力キャッシュをオフにしました。その結果、アプリケーションは数日間、セッション間での問題が 1 つも発生することなく実行されました。その後、2 年以上エラーなく動作しました。異なるアプリケーションと異なる Web サーバーのセットを使用する別の会社では、まったく同じ問題が解消されるのを確認しました。 Contoso.com と同様に、出力キャッシュを削除すると問題が解決します。
Microsoft は後に、この動作が OutputCacheModule の問題に起因することを確認しました。 (この記事を読んでいる時点で更新プログラムがリリースされている可能性があります。) ASP.NET を IIS 6.0 で使用し、カーネル モード キャッシュが有効になっている場合、OutputCacheModule は、渡したキャッシュされた応答から Set-Cookie ヘッダーを削除できないことがあります。 Http.sys に。エラーが発生する具体的な一連のイベントは次のとおりです。
• 最近サイトにアクセスしたことがない (したがって、対応するセッションを持たない) ユーザーが、出力キャッシュが有効になっているが、出力が現在利用できないページを要求します。キャッシュ内にあります。
• リクエストは、ユーザーが最近作成したセッションにアクセスするコードを実行し、セッション ID Cookie が応答の Set-Cookie ヘッダーで返されます。
? OutputCacheModule は出力を Http.sys に提供しますが、応答から Set-Cookie ヘッダーを削除できません。
? Http.sys は後続の要求に対してキャッシュされた応答を返し、他のユーザーを誤ってセッションに接続します。
この話の教訓は、セッション状態とカーネルモードの出力キャッシュは混同しないということです。出力キャッシュが有効になっているページでセッション状態を使用し、アプリケーションが IIS 6.0 で実行されている場合は、カーネル モードの出力キャッシュをオフにする必要があります。出力キャッシュの恩恵は受けられますが、カーネル モードの出力キャッシュは通常の出力キャッシュよりもはるかに高速であるため、キャッシュの効率はそれほど高くありません。この問題の詳細については、support.microsoft.com/kb/917072 を参照してください。
ページの OutputCache ディレクティブに VaryByParam="*" 属性を含めることで、個々のページのカーネル モード出力キャッシュをオフにすることができますが、そうするとメモリ要件が突然増加する可能性があります。もう 1 つのより安全な方法は、web.config に次の要素を含めることにより、アプリケーション全体のカーネル モード キャッシュをオフにすることです:
レジストリ設定を使用して、カーネル モード出力キャッシュをグローバルに無効にすることもできます。つまり、すべてのサーバーに対してカーネル モード出力キャッシュを無効にすることもできます。詳細については、support.microsoft.com/kb/820129 を参照してください。
顧客が不可解なセッションの問題を報告するのを聞くたびに、私は、ページで出力キャッシュを使用しているかどうかを尋ねます。出力キャッシュを使用しており、ホスト OS が Windows Server 2003 である場合は、カーネル モードの出力キャッシュを無効にすることをお勧めします。問題は通常解決されます。問題が解決しない場合は、コードにバグが存在します。
トップに
戻る
フォーム認証チケットの有効期間 次のコードの問題を特定できますか?
FormsAuthentication.RedirectFromLoginPage(ユーザー名、true);
このコードは問題ないように見えますが、アプリケーション内の他の場所のコードがこのステートメントの悪影響を相殺しない限り、ASP.NET 1.x アプリケーションでは決して使用しないでください。理由がわからない場合は、読み続けてください。
FormsAuthentication.RedirectFromLoginPage は 2 つのタスクを実行します。まず、FormsAuthenticationModule がユーザーをログイン ページにリダイレクトすると、FormsAuthentication.RedirectFromLoginPage はユーザーを最初に要求したページにリダイレクトします。 2 番目に、ユーザーが所定の期間認証を維持できるようにする認証チケット (通常は Cookie で保持され、ASP.NET 1.x では常に Cookie で保持されます) を発行します。
問題はこの時期にあります。 ASP.NET 1.x では、false の別のパラメーターを RedirectFromLoginPage に渡すと、デフォルトで 30 分後に期限切れになる一時的な認証チケットが発行されます。 (web.config の要素の Timeout 属性を使用してタイムアウト期間を変更できます。) ただし、別のパラメーターに true を渡すと、50 年間有効な永続的な認証チケットが発行されます。これにより、誰かがその認証を盗んだ場合に問題が発生します。チケットを発行すると、チケットの有効期間中、被害者の ID を使用して Web サイトにアクセスできます。認証チケットを盗む方法は数多くあります。公衆無線アクセス ポイント上の暗号化されていないトラフィックを調査する、Web サイト全体でスクリプトを実行する、被害者のコンピュータに物理的にアクセスするなどです。そのため、RedirectFromLoginPage に true を渡す方が、Web サイトを無効にするよりも安全です。幸いなことに、この問題は ASP.NET 2.0 で解決されました。 RedirectFromLoginPage は、一時的および永続的な認証チケットに対して web.config で指定されたタイムアウトを同様に受け入れるようになりました。
解決策の 1 つは、ASP.NET 1.x アプリケーションで RedirectFromLoginPage の 2 番目のパラメーターに true を渡さないことです。しかし、これは現実的ではありません。ログイン ページには、ユーザーがチェックを入れると、一時的な認証 Cookie ではなく永続的な認証 Cookie を受け取ることができる「ログイン状態を維持する」ボックスが表示されることがよくあります。もう 1 つの解決策は、Global.asax (または必要に応じて HTTP モジュール) のコード スニペットを使用することです。これにより、ブラウザーに返される前に永続認証チケットを含む Cookie が変更されます。
図 3 には、そのようなコード スニペットが 1 つ含まれています。このコード スニペットが Global.asax にある場合、送信される永続的なフォーム認証 Cookie の Expires プロパティが変更され、Cookie が 24 時間後に期限切れになるようになります。 「新しい有効期限」とコメントされた行を変更することで、タイムアウトを任意の日付に設定できます。
Application_EndRequest メソッドがローカル ヘルパー メソッド (GetCookieFromResponse) を呼び出して、発信応答の認証 Cookie をチェックするのは奇妙に思われるかもしれません。 Helper メソッドは、存在しない Cookie をチェックするために HttpCookieCollection の文字列インデックス ジェネレーターを使用した場合に、偽の Cookie が応答に追加されるという ASP.NET 1.1 の別のバグの回避策です。 GetCookieFromResponse として整数インデックス ジェネレーターを使用すると、問題が解決されます。
トップに戻る ビュー ステート: サイレント パフォーマンス キラー ある意味、ビュー ステートはこれまでで最も優れたものです。結局のところ、ビュー ステートを使用すると、ページとコントロールがポストバック間で状態を維持できるようになります。したがって、従来の ASP のように、ボタンをクリックしたときにテキスト ボックス内のテキストが消えるのを防ぐためのコードを作成したり、ポストバック後にデータベースを再クエリして DataGrid を再バインドしたりする必要はありません。
しかし、ビューステートには欠点があります。大きくなりすぎると、静かにパフォーマンスを低下させることになります。テキスト ボックスなどの一部のコントロールは、ビューステートに基づいて決定を行います。他のコントロール (特に DataGrid と GridView) は、表示される情報量に基づいてビュー状態を決定します。 GridView に 200 行や 300 行のデータが表示されたら、私は気が遠くなるでしょう。 ASP.NET 2.0 ビュー ステートのサイズは ASP.NET 1.x ビュー ステートの約半分ですが、GridView が不良であると、ブラウザと Web サーバー間の接続の実効帯域幅が簡単に 50% 以上減少する可能性があります。
EnableViewState を false に設定すると、個々のコントロールのビュー ステートをオフにできますが、一部のコントロール (特に DataGrid) はビュー ステートを使用できないと一部の機能が失われます。ビューステートを制御するためのより良い解決策は、ビューステートをサーバー上に保持することです。 ASP.NET 1.x では、ページの LoadPageStateFromPersistenceMedium メソッドと SavePageStateToPersistenceMedium メソッドをオーバーライドして、ビュー ステートを好みの方法で処理できました。図 4 のコードは、ビュー ステートが非表示フィールドに保持されるのを防ぎ、代わりにセッション ステートに保持するオーバーライドを示しています。セッション状態へのビュー ステートの保存は、既定のセッション状態プロセス モデルで使用する場合 (つまり、セッション状態がメモリ内の ASP.NET ワーカー プロセスに保存される場合)、特に効果的です。対照的に、セッション状態がデータベースに保存されている場合は、セッション状態にビュー ステートを保持することでパフォーマンスが向上するか低下するかをテストするだけでわかります。
ASP.NET 2.0 でも同じアプローチが使用されていますが、ASP.NET 2.0 では、セッション状態でビュー状態を保持する簡単な方法が提供されています。まず、GetStatePersister メソッドが .NET Framework SessionPageStatePersister クラスのインスタンスを返すカスタム ページ アダプターを定義します。
public class SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public override PageStatePersister GetStatePersister () {return new SessionPageStatePersister(this.Page ) ; }}
次に、次のように App.browsers ファイルをアプリケーションの App_Browsers フォルダーに配置して、カスタム ページ アダプターをデフォルトのページ アダプターとして登録します。
(拡張子 .browsers が付いている限り、ファイルには任意の名前を付けることができます。) その後、ASP.NET はページ アダプターを読み込み、返された SessionPageStatePersister を使用して、ビュー ステートを含むすべてのページ状態を保存します。
カスタム ページ アダプターを使用する場合の欠点の 1 つは、カスタム ページ アダプターがアプリケーション内のすべてのページにグローバルに適用されることです。一部のページのビュー ステートをセッション状態に維持し、他のページのビュー ステートを維持したくない場合は、図 4 に示す方法を使用します。また、ユーザーが同じセッションで複数のブラウザ ウィンドウを作成した場合、この方法を使用すると問題が発生する可能性があります。
トップに戻る
SQL Server セッション状態: もう 1 つのパフォーマンスキラー
ASP.NET では、セッション状態をデータベースに簡単に保存できます。web.config でスイッチを切り替えるだけで、セッション状態がバックエンド データベースに簡単に移動されます。これは、レルム内のすべてのサーバーがセッション状態の共通リポジトリを共有できるため、Web レルムで実行されているアプリケーションにとって重要な機能です。データベース アクティビティが追加されると、個々のリクエストのパフォーマンスが低下しますが、スケーラビリティの向上によりパフォーマンスの低下が補われます。
これはすべて良いように聞こえますが、いくつかの点を考慮すると状況は変わります。
• セッション状態を使用するアプリケーションであっても、ほとんどのページはセッション状態を使用しません。
? 既定では、ASP.NET セッション ステート マネージャーは、要求されたページがセッション ステートを使用するかどうかに関係なく、各要求でセッション データ ストアに対して 2 つのアクセス (読み取りアクセス 1 つと書き込みアクセス 1 つ) を実行します。
つまり、SQL Server™ セッション状態オプションを使用すると、セッション状態と関係のないページに対するリクエストであっても、すべてのリクエストに対して料金 (2 回のデータベース アクセス) を支払うことになります。これは、Web サイト全体のスループットに直接的な悪影響を及ぼします。
図 5 不必要なセッション状態データベースへのアクセスを排除する
では、どうすればよいでしょうか?方法は簡単です。セッション状態を使用しないページではセッション状態を無効にします。これは常に良い考えですが、セッション状態がデータベースに保存されている場合は特に重要です。図 5 は、セッション状態を無効にする方法を示しています。ページがセッション状態をまったく使用しない場合は、次のように Page ディレクティブに EnableSessionState="false" を含めます:
<%@ Page EnableSessionState="false" ... %>
このディレクティブにより、セッション状態マネージャーがリクエストごとにセッション状態データベースの読み取りと書き込みを行うことができなくなります。ページがセッション状態からデータを読み取るが、データを書き込まない (つまり、ユーザーのセッションの内容を変更しない) 場合は、次のように EnableSessionState を ReadOnly に設定します:
<%@ Page EnableSessionState="ReadOnly" ... %>
最後に、ページがセッション状態への読み取り/書き込みアクセスを必要とする場合は、EnableSessionState プロパティを省略するか、true に設定します:
<%@ Page EnableSessionState="true" ... %>
この方法でセッション状態を制御することで、ASP.NET が本当に必要な場合にのみセッション状態データベースにアクセスできるようになります。不必要なデータベース アクセスを排除することは、高パフォーマンスのアプリケーションを構築するための第一歩です。
ちなみに、EnableSessionState プロパティは public です。このプロパティは ASP.NET 1.0 以降に文書化されていますが、開発者がそれを利用しているのをまだほとんど見かけません。おそらく、メモリ内のデフォルトのセッション状態モデルにとってはそれほど重要ではないためです。ただし、SQL Server モデルにとっては重要です。
トップに戻る キャッシュされていないロール 次のステートメントは、ASP.NET 2.0 アプリケーションの web.config ファイルや、ASP.NET 2.0 ロール マネージャーを紹介する例によく現れます
。
ただし、上で示したように、このステートメントはパフォーマンスに重大な悪影響を及ぼします。理由を知っていますか?
デフォルトでは、ASP.NET 2.0 ロール マネージャーはロール データをキャッシュしません。代わりに、ユーザーがどのロールに属しているかを判断する必要があるたびに、ロール データ ストアを参照します。これは、ユーザーが認証されると、ロール データを利用するすべてのページ (たとえば、セキュリティ クリッピング設定が有効になっているサイトマップを使用するページや、web.config のロールベースの URL ディレクティブを使用してアクセスが制限されているページ) がロールの原因となることを意味します。マネージャーがロール データ ストアをクエリします。ロールがデータベースに保存されている場合は、リクエストごとに複数のデータベースにアクセスする手間を簡単に省くことができます。解決策は、ロール データを Cookie にキャッシュするようにロール マネージャーを構成することです
。
他の
トップに戻る構成ファイルのプロパティのシリアル化
ASP.NET 2.0 プロファイル サービスは、個人用設定や言語設定など、ユーザーごとの状態を維持する問題に対する既成のソリューションを提供します。プロファイル サービスを使用するには、個々のユーザーに代わって保持する属性を含む XML プロファイルを定義します。次に、ASP.NET は同じプロパティを含むクラスをコンパイルし、ページに追加された構成ファイルのプロパティを通じてクラス インスタンスへの厳密に型指定されたアクセスを提供します。
プロファイルの柔軟性は非常に優れているため、カスタム データ型をプロファイル プロパティとして使用することもできます。ただし、開発者が間違いを犯す原因となる問題を私が個人的に見てきました。図 6 には、Posts という名前の単純なクラスと、Posts をプロファイル属性として使用するプロファイル定義が含まれています。ただし、このクラスとこの構成ファイルは実行時に予期しない動作を引き起こします。理由がわかりますか?
問題は、Posts に _count というプライベート フィールドが含まれていることです。クラス インスタンスを完全にフリーズおよび再フリーズするには、このフィールドをシリアル化および逆シリアル化する必要があります。ただし、_count はプライベートであり、ASP.NET プロファイル マネージャーは既定で XML シリアル化を使用してカスタム型をシリアル化および逆シリアル化するため、シリアル化および逆シリアル化されません。 XML シリアライザーは非パブリック メンバーを無視します。したがって、Posts のインスタンスはシリアル化および逆シリアル化されますが、クラス インスタンスが逆シリアル化されるたびに、_count は 0 にリセットされます。
1 つの解決策は、_count をプライベート フィールドではなくパブリック フィールドにすることです。別の解決策は、_count をパブリック読み取り/書き込みプロパティでカプセル化することです。最善の解決策は、(SerializableAttribute を使用して) Post をシリアル化可能としてマークし、.NET Framework バイナリ シリアライザーを使用してクラス インスタンスをシリアル化および逆シリアル化するようにプロファイル マネージャーを構成することです。このソリューションでは、クラス自体の設計が維持されます。 XML シリアライザーとは異なり、バイナリ シリアライザーはフィールドがアクセス可能かどうかに関係なくフィールドをシリアル化します。図 7 は、Posts クラスの修正バージョンを示し、変更された付随するプロファイル定義を強調表示しています。
留意すべき点の 1 つは、カスタム データ型をプロファイル プロパティとして使用しており、そのデータ型にその型のインスタンスを完全にシリアル化するためにシリアル化する必要がある非パブリック データ メンバーが含まれている場合は、serializeAs= を使用することです。 Binary」をプロパティ宣言プロパティに追加し、型自体がシリアル化可能であることを確認します。そうしないと、完全なシリアル化が行われず、プロファイルが機能しない理由を特定するのに時間を無駄にすることになります。
先頭に戻る スレッド プールの飽和度 データベース クエリを実行し、クエリ結果が返されるまで 15 秒以上待機しているときに表示される ASP.NET ページの実際の数に非常に驚かれることがよくあります。 (クエリの結果が表示されるまで 15 分も待ちました!) 遅延は、大量のデータが返されることによる避けられない結果である場合もあれば、データベースの設計が不十分であることが原因である場合もあります。ただし、理由に関係なく、長いデータベース クエリやあらゆる種類の長い I/O 操作により、ASP.NET アプリケーションのスループットが低下します。
この問題については以前に詳しく説明したので、ここでは詳しく説明しません。 ASP.NET は、限られたスレッド プールに依存して要求を処理していると言えれば、すべてのスレッドがデータベース クエリ、Web サービス呼び出し、またはその他の I/O 操作の完了を待って占有されている場合、それらのスレッドは操作が完了すると解放されます。スレッドが発行される前に、他のリクエストがキューに入れられて待機している必要があります。リクエストがキューに入れられると、パフォーマンスが大幅に低下します。キューがいっぱいの場合、ASP.NET は後続の要求を HTTP 503 エラーで失敗させます。これは、運用 Web サーバー上の運用アプリケーションでは望ましくない状況です。
解決策は非同期ページです。これは、ASP.NET 2.0 の優れた機能の 1 つですが、あまり知られていません。非同期ページの要求はスレッド上で開始されますが、I/O 操作が開始されると、そのスレッドと ASP.NET の IAsyncResult インターフェイスに戻ります。操作が完了すると、要求は IAsyncResult を通じて ASP.NET に通知され、ASP.NET はプールから別のスレッドをプルして要求の処理を完了します。 I/O 操作が発生するとき、スレッド プールのスレッドが占有されないことに注意してください。これにより、他のページ (時間のかかる I/O 操作を実行していないページ) に対するリクエストがキュー内で待機することがなくなり、スループットが大幅に向上します。
非同期ページについては、MSDN® マガジン 2005 年 10 月号ですべて読むことができます。マシンバウンドではなく I/O バウンドで、実行に時間がかかるページは、非同期ページになる可能性が高くなります。
開発者に非同期ページについて話すと、「それは素晴らしいことですが、私のアプリケーションではそれらは必要ありません。」と答えることがよくあります。
Web サービスでは
、ASP.NET のパフォーマンス カウンターでキューに入れられたリクエストと平均待機時間の統計を確認しましたか?
実際の ASP.NET アプリケーションの多くは非同期ページを必要とします。これを覚えておいてください!
上に戻る 偽装と ACL 認可 以下は単純な構成ディレクティブですが、web.config でこれを見るたびに目が光ります:
このディレクティブにより、ASP.NET アプリケーションでのクライアント側の偽装が有効になります。これにより、クライアントを表すアクセス トークンがリクエストを処理するスレッドに添付され、オペレーティング システムによって実行されるセキュリティ チェックがワーカー プロセス ID ではなくクライアント ID に対して行われるようになります。 ASP.NET アプリケーションでモックが必要になることはほとんどありません。私の経験では、開発者は誤った理由でモックを有効にすることがよくあります。その理由は次のとおりです。
開発者は多くの場合、ファイル システムのアクセス許可を使用してページへのアクセスを制限できるように、ASP.NET アプリケーションで偽装を有効にします。 Bob が Salaries.aspx を表示する権限を持っていない場合、開発者は、Bob の読み取り権限を拒否するようにアクセス制御リスト (ACL) を設定することで、Bob が Salaries.aspx を表示できないように偽装を有効にします。ただし、次のような隠れた危険があります。ACL 認証には偽装は不要です。 ASP.NET アプリケーションで Windows 認証を有効にすると、ASP.NET は要求された各 .aspx ページの ACL を自動的にチェックし、ファイルを読み取るアクセス許可を持たない呼び出し元からの要求を拒否します。シミュレーションが無効になっている場合でも、このように動作します。
場合によっては、シミュレーションを正当化する必要があります。しかし、通常は適切な設計を行うことでそれを回避できます。たとえば、Salaries.aspx が、管理者のみが知っている給与情報をデータベースにクエリするとします。偽装を使用すると、データベース権限を使用して、管理職以外の従業員が給与データをクエリする権限を拒否できます。または、Salaries.aspx に ACL を設定して、管理者以外のユーザーが読み取りアクセスできないようにすることで、偽装を無視し、給与データへのアクセスを制限することもできます。後者のアプローチはモックを完全に回避するため、パフォーマンスが向上します。また、不必要なデータベースへのアクセスも排除されます。セキュリティ上の理由だけでデータベースへのクエリが拒否されるのはなぜですか?
ちなみに、私はかつて、無制限のメモリ使用量が原因で定期的に再起動していた従来の ASP アプリケーションのトラブルシューティングを支援したことがあります。経験の浅い開発者は、クエリ対象のテーブルに大きくて多数の画像が含まれていることを考慮せずに、ターゲットの SELECT ステートメントを SELECT * に変換しました。問題は、検出されていないメモリ リークによってさらに悪化します。 (私の領域はマネージド コードです!) 何年も正常に動作していたアプリケーションが突然動作しなくなりました。これは、以前は 1 ~ 2 キロバイトのデータを返していた SELECT ステートメントが数メガバイトを返すようになったためです。これに不適切なバージョン管理の問題が加わると、開発チームの活動は「多動」になる必要があります。「多動」というのは、自分がゲームをしている間、子供たちが迷惑なゲームをプレイしているのを見なければならないようなものです。夜はベッドで退屈なサッカーの試合。
理論上、マネージド コードのみで構成される ASP.NET アプリケーションでは従来のメモリ リークは発生しません。ただし、メモリ使用量が不十分であると、ガベージ コレクションがより頻繁に発生するため、パフォーマンスに影響を与える可能性があります。 ASP.NET アプリケーションでも SELECT * には注意してください。
上に戻る SELECT * に全面的に依存しないでください。データベースの構成ファイルをセットアップしてください。
コンサルタントとして、私はアプリケーションが期待どおりに動作しない理由をよく尋ねられます。最近、ある人が私のチームに、なぜ ASP.NET アプリケーションがドキュメントの要求に必要なスループット (1 秒あたりのリクエスト数) の約 1/100 しか完了していないのかと尋ねました。私たちがこれまでに発見した問題は、適切に動作しなかった Web アプリケーションで発生した問題に特有のものであり、私たち全員が真剣に受け止めるべき教訓です。
SQL Server Profiler を実行し、このアプリケーションとバックエンド データベース間の対話を監視します。より極端なケースでは、ボタンを 1 回クリックするだけで、データベース内で 1,500 を超えるエラーが発生しました。高性能アプリケーションをそのように構築することはできません。優れたアーキテクチャは、常に優れたデータベース設計から始まります。コードがどれほど効率的であっても、書かれていないデータベースによって重量が減っても機能しません。
通常、データアクセスアーキテクチャが不十分なのは、次の1つ以上に起因します。
•データベース設計が不十分です(通常、データベース管理者ではなく開発者によって設計されています)。
•データセットとDataAdapterの使用 - 特にDataAdapter.Updateは、Windowsフォームのアプリケーションやその他のリッチクライアントに適していますが、一般的にWebアプリケーションには理想的ではありません。
•計算が不十分であり、比較的単純な操作を実行するために多くのCPUサイクルを消費する設計不十分なデータアクセスレイヤー(DAL)。
問題は、治療する前に特定する必要があります。データアクセスの問題を識別する方法は、SQL Serverプロファイラーまたは同等のツールを実行して、舞台裏で何が起こっているかを確認することです。アプリケーションとデータベースの間の通信をチェックした後、パフォーマンスチューニングは完了します。試してみてください。あなたが見つけたものに驚くかもしれません。
一番の結論に戻ると、ASP.NETの生産アプリケーションを構築する際に遭遇する可能性のある問題とその解決策のいくつかを知っています。次のステップは、あなた自身のコードを詳しく見て、ここで概説した問題のいくつかを避けるようにすることです。 ASP.NETは、Web開発者のエントリへの障壁を下げた可能性がありますが、アプリケーションには柔軟で安定し、効率的であるためのあらゆる理由があります。初心者の間違いを避けるために、これを注意深く検討してください。
図8は、この記事で説明されている落とし穴を回避するために使用できる短いチェックリストを示しています。同様のセキュリティ欠陥チェックリストを作成できます。例:
•機密データを含む構成セクションを暗号化しましたか?
•データベース操作で使用されている入力をチェックして検証していますか?また、HTMLエンコード入力を出力として使用していますか?
•仮想ディレクトリには、保護されていない拡張機能を備えたファイルが含まれていますか?
これらの質問は、ウェブサイト、ホストするサーバー、および依存しているバックエンドリソースの整合性を大切にする場合に重要です。
Jeff Prosiseは、MSDNマガジンの寄稿編集者であり、Microsoft .Netのプログラミング(Microsoft Press、2002)を含むいくつかの本の著者です。彼はまた、ソフトウェアコンサルティングおよび教育会社であるWintellectの共同設立者でもあります。
MSDNマガジンの2006年7月号から。