Share via


アプリケーションの起動時にデータをキャッシュする (C#)

作成者: Scott Mitchell

PDF のダウンロード

Web アプリケーションでは、一部のデータが頻繁に使用され、一部のデータは頻繁に使用されません。 キャッシュと呼ばれる手法である頻繁に使用されるデータを事前に読み込むことで、ASP.NET アプリケーションのパフォーマンスを向上させることができます。 このチュートリアルでは、プロアクティブ読み込みの 1 つの方法を示します。これは、アプリケーションの起動時にキャッシュにデータを読み込む方法です。

はじめに

前の 2 つのチュートリアルでは、プレゼンテーション レイヤーとキャッシュ レイヤーでのデータのキャッシュについて説明しました。 「ObjectDataSource を使用したデータのキャッシュ」では、ObjectDataSource のキャッシュ機能を使用してプレゼンテーション レイヤーにデータをキャッシュする方法について説明しました。 アーキテクチャ内のデータのキャッシュ では、新しい個別のキャッシュ レイヤーでのキャッシュが調べられた。 これらのチュートリアルではどちらも、データ キャッシュの操作で 事後対応型の読み込みを 使用しました。 事後対応型読み込みでは、データが要求されるたびに、システムは最初にキャッシュ内にあるかどうかを確認します。 そうでない場合は、データベースなどの元のソースからデータを取得し、キャッシュに格納します。 反応性読み込みのメイン利点は、実装の容易さです。 その欠点の 1 つは、要求間でパフォーマンスが不均等になることです。 前のチュートリアルのキャッシュ レイヤーを使用して製品情報を表示するページを想像してみてください。 このページが初めてアクセスされたとき、またはメモリ制約または指定された有効期限に達したためにキャッシュされたデータが削除された後に初めてアクセスした場合は、データベースからデータを取得する必要があります。 したがって、これらのユーザー要求は、キャッシュで提供できるユーザー要求よりも時間がかかります。

プロアクティブ読み込み では、必要になる前にキャッシュされたデータを読み込むことで、要求全体のパフォーマンスを滑らかにする別のキャッシュ管理戦略が提供されます。 通常、プロアクティブ読み込みでは、基になるデータの更新が行われたときに定期的にチェックされるか通知されるプロセスが使用されます。 このプロセスでは、キャッシュが最新の状態に保たれ、更新されます。 プロアクティブ読み込みは、基になるデータが低速なデータベース接続、Web サービス、またはその他の特に低速なデータ ソースから取得される場合に特に便利です。 ただし、プロアクティブ読み込みに対するこのアプローチは、変更をチェックしてキャッシュを更新するためのプロセスを作成、管理、デプロイする必要があるため、実装が困難になります。

プロアクティブ読み込みのもう 1 つのフレーバーと、このチュートリアルで説明する種類は、アプリケーションの起動時にキャッシュにデータを読み込む方法です。 この方法は、データベース参照テーブルのレコードなどの静的データをキャッシュする場合に特に便利です。

注意

プロアクティブ読み込みと事後対応読み込みの違いと、長所、短所、実装に関する推奨事項の一覧については、「.NET Framework アプリケーションのキャッシュ アーキテクチャ ガイド」の「キャッシュの内容の管理」セクションを参照してください。

手順 1: アプリケーションの起動時にキャッシュするデータを決定する

前の 2 つのチュートリアルで調べた事後対応型読み込みを使用したキャッシュの例は、定期的に変更される可能性があり、生成に法外に長い時間はかからず、データとうまく連携します。 ただし、キャッシュされたデータが変更されない場合、リアクティブ読み込みによって使用される有効期限は余分です。 同様に、キャッシュされるデータの生成に非常に長い時間がかかる場合、キャッシュが空であると見なされる要求を持つユーザーは、基になるデータの取得中に長い待機に耐える必要があります。 アプリケーションの起動時に生成するのに非常に長い時間がかかる静的データとデータをキャッシュすることを検討してください。

データベースには動的で頻繁に変化する値が多数ありますが、ほとんどの場合、静的データも大量に含まれています。 たとえば、事実上すべてのデータ モデルには、固定された選択肢セットの特定の値を含む 1 つ以上の列があります。 Patientsデータベース テーブルには列がありPrimaryLanguage、値のセットは英語、スペイン語、フランス語、ロシア語、日本語などです。 多くの場合、これらの種類の列は 参照テーブルを使用して実装されます。 テーブルに英語またはフランス語 Patients の文字列を格納するのではなく、一般的に 2 つの列 (一意識別子と文字列の説明) を持つ 2 つ目のテーブルが作成され、使用可能な値ごとにレコードが含まれます。 テーブル内の列には PrimaryLanguagePatients 対応する一意識別子が参照テーブルに格納されます。 図 1 では、患者の John Doe の第一言語は英語で、Ed Johnson はロシア語です。

言語テーブルは、患者テーブルで使用される参照テーブルです

図 1: テーブルは Languages テーブルで Patients 使用される参照テーブルです

新しい患者を編集または作成するためのユーザー インターフェイスには、テーブル内のレコードによって設定された許容言語のドロップダウン リストが Languages 含まれます。 キャッシュを使用しない場合、このインターフェイスにアクセスするたびに、システムはテーブルに対してクエリを実行する Languages 必要があります。 ルックアップ テーブルの値は変更される頻度が非常に低いため、これは無駄で不要です。

前のチュートリアルで Languages 調べたのと同じ事後対応読み込み手法を使用して、データをキャッシュできます。 ただし、事後対応読み込みでは時間ベースの有効期限が使用されます。これは静的参照テーブル データには必要ありません。 リアクティブ読み込みを使用したキャッシュはキャッシュをまったく行っていないよりも優れていますが、最適な方法は、アプリケーションの起動時に参照テーブル のデータをキャッシュに事前に読み込む方法です。

このチュートリアルでは、参照テーブル データとその他の静的な情報をキャッシュする方法について説明します。

手順 2: データをキャッシュするさまざまな方法を調べる

情報は、さまざまな方法を使用して ASP.NET アプリケーションにプログラムでキャッシュできます。 前のチュートリアルでは、データ キャッシュの使用方法を既に確認しました。 または、 静的メンバー または アプリケーションの状態を使用して、オブジェクトをプログラムでキャッシュすることもできます。

クラスを操作する場合、通常、メンバーにアクセスするには、まず クラスをインスタンス化する必要があります。 たとえば、ビジネス ロジック レイヤーのいずれかのクラスからメソッドを呼び出すには、まず クラスのインスタンスを作成する必要があります。

ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";

SomeMethod を呼び出したり、SomeProperty を操作したりする前に、まず、キーワード (keyword)を使用して クラスのインスタンスを作成するnew必要があります。 SomeMethodSomeProperty は、特定のインスタンスに関連付けられます。 これらのメンバーの有効期間は、関連付けられているオブジェクトの有効期間に関連付けられます。 一方、静的メンバーは、クラスのすべてのインスタンス間で共有される変数、プロパティ、およびメソッドであり、したがって、 クラスと同じ有効期間を持ちます。 静的メンバーは、 キーワード (keyword) staticで示されます。

静的メンバーに加えて、アプリケーションの状態を使用してデータをキャッシュできます。 各 ASP.NET アプリケーションは、アプリケーションのすべてのユーザーとページで共有される名前/値コレクションを保持します。 このコレクションには、 クラスの Application プロパティHttpContext使用してアクセスでき、次のように ASP.NET ページの分離コード クラスから使用できます。

Application["key"] = value;
object value = Application["key"];

データ キャッシュは、データをキャッシュするためのより豊富な API を提供し、時間と依存関係に基づく有効期限、キャッシュ 項目の優先順位などのメカニズムを提供します。 静的メンバーとアプリケーションの状態では、このような機能をページ開発者が手動で追加する必要があります。 ただし、アプリケーションの起動時にアプリケーションの有効期間中にデータをキャッシュする場合、データ キャッシュの利点は問題になります。 このチュートリアルでは、静的データをキャッシュするために 3 つの手法をすべて使用するコードについて説明します。

手順 3: テーブル データのSuppliersキャッシュ

これまでに実装した Northwind データベース テーブルには、従来の参照テーブルは含まれていません。 DAL に実装されている 4 つの DataTable は、非静的な値を持つすべてのモデル テーブルです。 このチュートリアルでは、新しい DataTable を DAL に追加し、新しいクラスとメソッドを BLL に追加する時間を費やすのではなく、テーブルのデータが静的であるふりを Suppliers してみましょう。 そのため、アプリケーションの起動時にこのデータをキャッシュできます。

開始するには、 フォルダーに という名前 StaticCache.cs の新しいクラスを CL 作成します。

CL フォルダーに StaticCache.cs クラスを作成する

図 2: フォルダーにクラスをStaticCache.cs作成するCL

起動時にデータを適切なキャッシュ ストアに読み込むメソッドと、このキャッシュからデータを返すメソッドを追加する必要があります。

[System.ComponentModel.DataObject]
public class StaticCache
{
    private static Northwind.SuppliersDataTable suppliers = null;
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using a static member variable
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        suppliers = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return suppliers;
    }
}

上記のコードでは、静的メンバー変数 を使用して、 suppliersメソッドから SuppliersBLL 呼び出されるクラスの GetSuppliers() メソッドの結果を LoadStaticCache() 保持します。 メソッドは LoadStaticCache() 、アプリケーションの起動時に呼び出されることを意図しています。 このデータがアプリケーションの起動時に読み込まれたら、サプライヤー データを操作する必要があるページは、クラスの GetSuppliers() メソッドをStaticCache呼び出すことができます。 したがって、仕入先を取得するためのデータベースの呼び出しは、アプリケーションの開始時に 1 回だけ行われます。

静的メンバー変数をキャッシュ ストアとして使用するのではなく、アプリケーションの状態またはデータ キャッシュを使用することもできます。 次のコードは、アプリケーションの状態を使用するように再ツールされたクラスを示しています。

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using application state
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpContext.Current.Application["key"] = suppliersBLL.GetSuppliers();
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpContext.Current.Application["key"] as Northwind.SuppliersDataTable;
    }
}

では LoadStaticCache()、サプライヤー情報がアプリケーション変数 キーに格納されます。 これは、 からGetSuppliers()適切な型 (Northwind.SuppliersDataTable) として返されます。 アプリケーションの状態は、 を使用して Application["key"]ASP.NET ページの分離コード クラスでアクセスできますが、アーキテクチャでは、現在HttpContextの を取得するために を使用HttpContext.Current.Application["key"]する必要があります。

同様に、次のコードに示すように、データ キャッシュをキャッシュ ストアとして使用できます。

[System.ComponentModel.DataObject]
public class StaticCache
{
    public static void LoadStaticCache()
    {
        // Get suppliers - cache using the data cache
        SuppliersBLL suppliersBLL = new SuppliersBLL();
        HttpRuntime.Cache.Insert(
          /* key */                "key", 
          /* value */              suppliers, 
          /* dependencies */       null, 
          /* absoluteExpiration */ Cache.NoAbsoluteExpiration, 
          /* slidingExpiration */  Cache.NoSlidingExpiration, 
          /* priority */           CacheItemPriority.NotRemovable, 
          /* onRemoveCallback */   null);
    }
    [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
    public static Northwind.SuppliersDataTable GetSuppliers()
    {
        return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
    }
}

時間ベースの有効期限のない項目をデータ キャッシュに追加するには、 と System.Web.Caching.Cache.NoSlidingExpiration の値をSystem.Web.Caching.Cache.NoAbsoluteExpiration入力パラメーターとして使用します。 キャッシュ項目の優先順位を指定できるように、データ キャッシュのInsertメソッドのこの特定のオーバーロードが選択されました。 優先度は、使用可能なメモリが不足しているときにキャッシュからスカベンジする項目を決定するために使用されます。 ここでは、優先度 NotRemovableを使用します。これにより、このキャッシュ項目が清掃されないようにします。

注意

このチュートリアルのダウンロードでは、静的メンバー変数のアプローチを StaticCache 使用して クラスを実装します。 アプリケーションの状態とデータ キャッシュ手法のコードは、クラス ファイルのコメントで使用できます。

手順 4: アプリケーションの起動時にコードを実行する

Web アプリケーションが最初に起動したときにコードを実行するには、 という名前 Global.asaxの特別なファイルを作成する必要があります。 このファイルには、アプリケーション、セッション、および要求レベルのイベントのイベント ハンドラーを含めることができます。ここでは、アプリケーションの起動時に実行されるコードを追加できます。

Visual Studio のGlobal.asaxソリューション エクスプローラーで Web サイト プロジェクト名を右クリックし、[新しい項目の追加] を選択して、Web アプリケーションのルート ディレクトリにファイルを追加します。 [新しい項目の追加] ダイアログ ボックスで、[グローバル アプリケーション クラス] 項目の種類を選択し、[追加] ボタンをクリックします。

注意

プロジェクトにファイルが Global.asax 既にある場合、[グローバル アプリケーション クラス] 項目の種類は [新しい項目の追加] ダイアログ ボックスに表示されません。

Web アプリケーションのルート ディレクトリに Global.asax ファイルを追加する

図 3: Web アプリケーションの Global.asax ルート ディレクトリにファイルを追加する (クリックするとフルサイズの画像が表示されます)

既定 Global.asax のファイル テンプレートには、サーバー側 <script> タグ内に 5 つのメソッドが含まれています。

  • Application_Start は、Web アプリケーションが最初に起動したときに実行されます
  • Application_End アプリケーションのシャットダウン時に実行される
  • Application_Error ハンドルされない例外がアプリケーションに到達するたびに実行されます
  • Session_Start は、新しいセッションの作成時に実行されます
  • Session_End セッションが期限切れまたは破棄されたときに実行される

イベント ハンドラーは Application_Start 、アプリケーションのライフ サイクル中に 1 回だけ呼び出されます。 アプリケーションは、ASP.NET リソースがアプリケーションから初めて要求されるときに起動し、アプリケーションが再起動されるまで実行を続けます。これは、フォルダーの内容の変更、フォルダー内の内容App_Codeの変更Global.asax、ファイルの/Bin変更Web.configなどによって発生する可能性があります。 アプリケーションのライフ サイクルの詳細については、「ASP.NET アプリケーション ライフ サイクルの概要」を参照してください。

これらのチュートリアルでは、 メソッドにコードを追加するだけで Application_Start 済むので、他のコードを自由に削除してください。 ではApplication_Start、 クラスの LoadStaticCache() メソッドをStaticCache呼び出すだけで、サプライヤー情報を読み込んでキャッシュします。

<%@ Application Language="C#" %>
<script runat="server">
    void Application_Start(object sender, EventArgs e) 
    {
        StaticCache.LoadStaticCache();
    }
</script>

これですべて完了です。 アプリケーションの起動時に LoadStaticCache() 、メソッドは BLL からサプライヤー情報を取得し、静的メンバー変数 (またはクラスで使用したキャッシュ ストア) に StaticCache 格納します。 この動作を確認するには、 メソッドにブレークポイントを Application_Start 設定し、アプリケーションを実行します。 ブレークポイントは、アプリケーションの起動時にヒットすることに注意してください。 ただし、後続の要求では、 メソッドは Application_Start 実行されません。

ブレークポイントを使用して、Application_Start イベント ハンドラーが実行されていることを確認する

図 4: ブレークポイントを使用してイベント ハンドラーが実行されていることを Application_Start 確認する (クリックするとフルサイズの画像が表示されます)

注意

最初にデバッグを開始するときにブレークポイントに Application_Start ヒットしない場合は、アプリケーションが既に開始されているためです。 または Web.config ファイルを変更してアプリケーションを強制的にGlobal.asax再起動してから、もう一度やり直してください。 これらのファイルの末尾に空白行を追加 (または削除) するだけで、アプリケーションをすばやく再起動できます。

手順 5: キャッシュされたデータを表示する

この時点で、 StaticCache クラスには、アプリケーションの起動時にキャッシュされたサプライヤー データのバージョンがあり、その GetSuppliers() メソッドを介してアクセスできます。 プレゼンテーション レイヤーからこのデータを操作するには、ObjectDataSource を使用するか、ASP.NET ページの分離コード クラスからクラスのGetSuppliers()メソッドをプログラムで呼び出StaticCacheすことができます。 ObjectDataSource コントロールと GridView コントロールを使用して、キャッシュされたサプライヤー情報を表示する方法を見てみましょう。

まず、フォルダー内の AtApplicationStartup.aspx ページを Caching 開きます。 GridView をツールボックスからデザイナーにドラッグし、そのプロパティを IDSuppliers設定します。 次に、GridView のスマート タグから、 という名前 SuppliersCachedDataSourceの新しい ObjectDataSource を作成することを選択します。 クラスGetSuppliers()の メソッドを使用するように ObjectDataSource をStaticCache構成します。

StaticCache クラスを使用するように ObjectDataSource を構成する

図 5: クラスを使用するように ObjectDataSource を構成する StaticCache (フルサイズの画像を表示する をクリックします)

GetSuppliers() メソッドを使用して、キャッシュされたサプライヤー データを取得する

図 6: メソッドを GetSuppliers() 使用してキャッシュされた仕入先データを取得する (クリックするとフルサイズの画像が表示されます)

ウィザードが完了すると、Visual Studio によって、 の各データ フィールドに BoundFields が自動的に SuppliersDataTable追加されます。 GridView と ObjectDataSource の宣言型マークアップは、次のようになります。

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="SupplierID" />
        <asp:BoundField DataField="CompanyName" HeaderText="CompanyName" 
            SortExpression="CompanyName" />
        <asp:BoundField DataField="Address" HeaderText="Address" 
            SortExpression="Address" />
        <asp:BoundField DataField="City" HeaderText="City" 
            SortExpression="City" />
        <asp:BoundField DataField="Country" HeaderText="Country" 
            SortExpression="Country" />
        <asp:BoundField DataField="Phone" HeaderText="Phone" 
            SortExpression="Phone" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="StaticCache" />

図 7 は、ブラウザーを介して表示されるページを示しています。 出力は、BLL SuppliersBLL のクラスからデータをプルした場合と同じですが、 クラスを StaticCache 使用すると、アプリケーションの起動時にキャッシュされたサプライヤー データが返されます。 クラスの メソッドにブレークポイントを StaticCache 設定して、 GetSuppliers() この動作を確認できます。

キャッシュされたサプライヤー データが GridView に表示される

図 7: キャッシュされたサプライヤー データが GridView に表示される (フルサイズの画像を表示する をクリックします)

まとめ

ほとんどのデータ モデルには、かなりの量の静的データが含まれています。通常は、参照テーブルの形式で実装されます。 この情報は静的であるため、この情報を表示する必要があるたびにデータベースに継続的にアクセスする理由はありません。 さらに、静的な性質により、データをキャッシュするときに有効期限が切れる必要はありません。 このチュートリアルでは、このようなデータを取得し、データ キャッシュ、アプリケーションの状態、および静的メンバー変数を使用してキャッシュする方法について説明しました。 この情報は、アプリケーションの起動時にキャッシュされ、アプリケーションの有効期間中はキャッシュに残ります。

このチュートリアルと過去 2 つのチュートリアルでは、アプリケーションの有効期間中のデータのキャッシュと、時間ベースの有効期限の使用について説明しました。 ただし、データベース データをキャッシュする場合、時間ベースの有効期限が理想的よりも短い場合があります。 キャッシュを定期的にフラッシュするのではなく、基になるデータベース データが変更された場合にのみ、キャッシュされた項目を削除するのが最適です。 この理想は、SQL キャッシュの依存関係を使用して可能です。これについては、次のチュートリアルで説明します。

幸せなプログラミング!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。

特別な感謝

このチュートリアル シリーズは、多くの役立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、テレサ マーフィーと Zack Jones でした。 今後の MSDN 記事の確認に関心がありますか? その場合は、 に行mitchell@4GuysFromRolla.comをドロップしてください。