最も簡単なキャッシュ戦略は、キャッシュされたデータを指定した期間の後に期限切れにすることです。 ただし、この単純な方法は、キャッシュされたデータが基になるデータソースとの関連付けを維持しないため、結果として古いデータが過剰に保持されたり、現在のデータの有効期限が早すぎて(すぐに)切れることを意味します。 SqlCacheDependency クラスを使用すると、基になるデータが SQL データベースで変更されるまでデータがキャッシュされたままになります。 このチュートリアルでは、その方法について説明します。
イントロダクション
アーキテクチャチュートリアルの ObjectDataSource と キャッシュ データ を使用したデータのキャッシュに関するチュートリアルで調べたキャッシュ手法では、時間ベースの有効期限を使用して、指定した期間が経過した後にキャッシュからデータを削除しました。 この方法は、キャッシュのパフォーマンス向上とデータの制約とのバランスを取る最も簡単な方法です。 ページ開発者は、x 秒の有効期限を選択することで、キャッシュのパフォーマンス上の利点を x 秒だけ利用することを認めますが、データが最大 x 秒より長く古くなることはなくなります。 もちろん、静的データの場合、「アプリケーション起動時のデータのキャッシュ」チュートリアルで説明したように、x は Web アプリケーションの有効期間まで拡張できます。
データベース データをキャッシュする場合、多くの場合、時間ベースの有効期限が使いやすさのために選択されますが、多くの場合、ソリューションは不十分です。 理想的には、基になるデータがデータベースで変更されるまで、データベース データはキャッシュされたままになります。キャッシュは削除されます。 この方法では、キャッシュのパフォーマンス上の利点を最大化し、古いデータの期間を最小限に抑えます。 ただし、これらの利点を享受するには、基になるデータベース データがいつ変更されたかを認識し、対応する項目をキャッシュから削除するシステムが存在する必要があります。 ASP.NET 2.0 より前は、ページ開発者がこのシステムの実装を担当していました。
ASP.NET 2.0 は、対応するキャッシュされた項目を削除できるように、 SqlCacheDependency
クラス と、データベースで変更が発生したタイミングを判断するために必要なインフラストラクチャを提供します。 基になるデータがいつ変更されたかを判断するには、通知とポーリングの 2 つの手法があります。 通知とポーリングの違いについて説明した後、ポーリングをサポートするために必要なインフラストラクチャを作成し、宣言型およびプログラムによるシナリオで SqlCacheDependency
クラスを使用する方法について説明します。
通知とポーリングについて
データベース内のデータがいつ変更されたかを判断するには、通知とポーリングという 2 つの手法を使用できます。 通知を使用すると、クエリの最後の実行後に特定のクエリの結果が変更されたときに、データベースによって ASP.NET ランタイムに自動的にアラートが送信され、その時点でクエリに関連付けられているキャッシュされた項目が削除されます。 ポーリングでは、データベース サーバーは特定のテーブルが最後に更新された日時に関する情報を保持します。 ASP.NET ランタイムは、データベースを定期的にポーリングして、キャッシュに入力されてから変更されたテーブルを確認します。 データが変更されたテーブルには、関連するキャッシュ 項目が削除されています。
通知オプションはポーリングよりもセットアップが少なくて済み、テーブル レベルではなくクエリ レベルで変更を追跡するため、より細かく設定できます。 残念ながら、通知は Microsoft SQL Server 2005 の完全版 (つまり、Express 以外のエディション) でのみ使用できます。 ただし、ポーリング オプションは、7.0 から 2005 までの Microsoft SQL Server のすべてのバージョンで使用できます。 これらのチュートリアルでは SQL Server 2005 の Express エディションを使用するため、ポーリング オプションの設定と使用に重点を置きます。 SQL Server 2005 の通知機能に関するその他のリソースについては、このチュートリアルの最後にある「さらに読む」セクションを参照してください。
ポーリングでは、AspNet_SqlCacheTablesForChangeNotification
、tableName
、notificationCreated
の 3 つの列を持つ changeId
という名前のテーブルを含むようにデータベースを構成する必要があります。 このテーブルには、Web アプリケーションの SQL キャッシュ依存関係で使用する必要がある可能性があるデータを含む各テーブルの行が含まれています。
tableName
列はテーブルの名前を指定し、notificationCreated
は行がテーブルに追加された日時を示します。
changeId
列はint
型で、初期値は 0 です。 その値は、テーブルに対する変更のたびにインクリメントされます。
データベースには、 AspNet_SqlCacheTablesForChangeNotification
テーブルに加えて、SQL キャッシュの依存関係に含まれる可能性がある各テーブルにトリガーも含める必要があります。 これらのトリガーは、行が挿入、更新、または削除されるたびに実行され、テーブルの changeId
値が AspNet_SqlCacheTablesForChangeNotification
でインクリメントされます。
ASP.NET ランタイムは、changeId
オブジェクトを使用してデータをキャッシュするときに、テーブルの現在のSqlCacheDependency
を追跡します。 データベースは定期的にチェックされ、SqlCacheDependency
がデータベースの値と異なるchangeId
オブジェクトは削除されます。異なるchangeId
値は、データがキャッシュされてからテーブルに変更が加えられたことを示しているためです。
手順 1: aspnet_regsql.exe
コマンド ライン プログラムの探索
ポーリングアプローチでは、前述のインフラストラクチャ (定義済みのテーブル (AspNet_SqlCacheTablesForChangeNotification
)、いくつかのストアド プロシージャ、および Web アプリケーションの SQL キャッシュ依存関係で使用できる各テーブルのトリガーを含むデータベースをセットアップする必要があります。 これらのテーブル、ストアド プロシージャ、トリガーは、aspnet_regsql.exe
フォルダーにあるコマンド ライン プログラム $WINDOWS$\Microsoft.NET\Framework\version
を使用して作成できます。
AspNet_SqlCacheTablesForChangeNotification
テーブルと関連するストアド プロシージャを作成するには、コマンド ラインから次を実行します。
/* For SQL Server authentication... */
aspnet_regsql.exe -S server -U user -P password -d database -ed
/* For Windows Authentication... */
aspnet_regsql.exe -S server -E -d database -ed
注
これらのコマンドを実行するには、指定されたデータベース ログインが db_securityadmin
ロールと db_ddladmin
ロールに存在する必要があります。
たとえば、Windows 認証を使用して pubs
という名前のデータベース サーバー上の ScottsServer
という名前の Microsoft SQL Server データベースにポーリングするためのインフラストラクチャを追加するには、適切なディレクトリに移動し、コマンド ラインから次のように入力します。
aspnet_regsql.exe -S ScottsServer -E -d pubs -ed
データベース レベルのインフラストラクチャが追加されたら、SQL キャッシュの依存関係で使用されるテーブルにトリガーを追加する必要があります。
aspnet_regsql.exe
コマンド ライン プログラムをもう一度使用しますが、-t
スイッチを使用してテーブル名を指定します。-ed
スイッチを使用する代わりに、次のように-et
を使用します。
/* For SQL Server authentication... */
aspnet_regsql.exe -S <i>server</i>
-U <i>user</i> -P <i>password</i> -d <i>database</i> -t <i>tableName</i> -et
/* For Windows Authentication... */
aspnet_regsql.exe -S <i>server</i>
-E -d <i>database</i> -t <i>tableName</i> -et
authors
のtitles
データベースのpubs
テーブルとScottsServer
テーブルにトリガーを追加するには、次の値を使用します。
aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et
このチュートリアルでは、 Products
、 Categories
、および Suppliers
テーブルにトリガーを追加します。 手順 3 では、特定のコマンド ライン構文について説明します。
手順 2: Microsoft SQL Server 2005 Express Edition データベースの参照App_Data
aspnet_regsql.exe
コマンド ライン プログラムでは、必要なポーリング インフラストラクチャを追加するために、データベースとサーバー名が必要です。 ただし、 App_Data
フォルダーに存在する Microsoft SQL Server 2005 Express データベースのデータベースとサーバー名は何ですか? データベース名とサーバー名を検出する必要はなく、最も簡単な方法は、データベースを localhost\SQLExpress
データベース インスタンスにアタッチし、 SQL Server Management Studio を使用してデータの名前を変更することです。 コンピューターに SQL Server 2005 の完全なバージョンのいずれかがインストールされている場合は、コンピューターに SQL Server Management Studio が既にインストールされている可能性があります。 Express エディションのみを使用している場合は、無料の Microsoft SQL Server Management Studio をダウンロードできます。
まず、Visual Studio を閉じます。 次に、SQL Server Management Studio を開き、Windows 認証を使用して localhost\SQLExpress
サーバーに接続することを選択します。
図 1: localhost\SQLExpress
サーバーにアタッチする
サーバーに接続すると、Management Studio によってサーバーが表示され、データベースやセキュリティなどのサブフォルダーが表示されます。 [データベース] フォルダーを右クリックし、[アタッチ] オプションを選択します。 [データベースのアタッチ] ダイアログ ボックスが表示されます (図 2 を参照)。 [追加] ボタンをクリックし、Web アプリケーションの NORTHWND.MDF
フォルダー内の App_Data
データベース フォルダーを選択します。
図 2: NORTHWND.MDF
フォルダーからApp_Data
データベースをアタッチします (フルサイズの画像を表示する をクリックします)。
これにより、データベースが [データベース] フォルダーに追加されます。 データベース名は、データベース ファイルへの完全なパス、または GUID で始まる完全なパスである場合があります。 aspnet_regsql.exe コマンド ライン ツールを使用するときに、この長いデータベース名を入力する必要がないようにするには、アタッチしたデータベースを右クリックし、[名前の変更] を選択して、データベースの名前をわかりやすい名前に変更します。 データベースの名前を DataTutorials に変更しました。
図 3: アタッチされたデータベースの名前を Human-Friendly 名に変更する
手順 3: Northwind データベースへのポーリング インフラストラクチャの追加
NORTHWND.MDF
フォルダーからApp_Data
データベースをアタッチしたので、ポーリング インフラストラクチャを追加する準備ができました。 データベースの名前を DataTutorials に変更したと仮定して、次の 4 つのコマンドを実行します。
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -ed
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Products -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Categories -et
aspnet_regsql.exe -S localhost\SQLExpress -E -d DataTutorials -t Suppliers -et
これらの 4 つのコマンドを実行した後、Management Studio でデータベース名を右クリックし、[タスク] サブメニューに移動し、[デタッチ] を選択します。 次に、Management Studio を閉じ、Visual Studio をもう一度開きます。
Visual Studio が再度開いたら、サーバー エクスプローラーを使用してデータベースにドリルダウンします。 新しいテーブル (AspNet_SqlCacheTablesForChangeNotification
)、新しいストアド プロシージャ、および Products
、 Categories
、および Suppliers
テーブルのトリガーに注意してください。
図 4: データベースに必要なポーリング インフラストラクチャが含まれるようになりました
手順 4: ポーリング サービスの構成
必要なテーブル、トリガー、ストアド プロシージャをデータベースに作成した後、最後の手順はポーリング サービスを構成することです。ポーリング サービスは、使用するデータベースとポーリング頻度をミリ秒単位で指定して Web.config
によって行われます。 次のマークアップは、Northwind データベースを 1 秒に 1 回ポーリングします。
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="NORTHWNDConnectionString" connectionString=
"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;
Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
...
<!-- Configure the polling service used for SQL cache dependencies -->
<caching>
<sqlCacheDependency enabled="true" pollTime="1000" >
<databases>
<add name="NorthwindDB"
connectionStringName="NORTHWNDConnectionString" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
name
要素 (NorthwindDB) の<add>
値は、人間が判読できる名前を特定のデータベースに関連付けます。 SQL キャッシュの依存関係を使用する場合は、ここで定義されているデータベース名と、キャッシュされたデータの基になるテーブルを参照する必要があります。 手順 6 では、 SqlCacheDependency
クラスを使用して SQL キャッシュの依存関係をキャッシュされたデータにプログラムで関連付ける方法について説明します。
SQL キャッシュの依存関係が確立されると、ポーリング システムは、 <databases>
要素で定義されているデータベースに pollTime
ミリ秒ごとに接続し、 AspNet_SqlCachePollingStoredProcedure
ストアド プロシージャを実行します。 このストアド プロシージャは、aspnet_regsql.exe
コマンド ライン ツールを使用して手順 3 で戻されましたが、tableName
内の各レコードのchangeId
とAspNet_SqlCacheTablesForChangeNotification
の値を返します。 古い SQL キャッシュの依存関係がキャッシュから削除されます。
pollTime
設定では、パフォーマンスとデータの制約のトレードオフが生じます。
pollTime
値が小さいと、データベースに対する要求の数が増えますが、キャッシュから古いデータが迅速に削除されます。
pollTime
値を大きくすると、データベース要求の数は減りますが、バックエンド データが変更されてから、関連するキャッシュ項目が削除されるまでの遅延が長くなります。 さいわい、データベース要求では、単純な軽量テーブルから数行のみを返す単純なストアド プロシージャが実行されています。 ただし、さまざまな pollTime
値を試して、アプリケーションのデータベース アクセスとデータの制約の理想的なバランスを見つけてください。 使用できる最小 pollTime
値は 500 です。
注
上記の例では、pollTime
要素に 1 つの<sqlCacheDependency>
値が提供されますが、必要に応じて、pollTime
要素に<add>
値を指定できます。 これは、複数のデータベースを指定し、データベースごとのポーリング頻度をカスタマイズする場合に便利です。
手順 5: SQL キャッシュの依存関係を宣言的に操作する
手順 1 から 4 では、必要なデータベース インフラストラクチャをセットアップし、ポーリング システムを構成する方法について説明しました。 このインフラストラクチャが整ったので、プログラムまたは宣言型の手法を使用して、関連する SQL キャッシュ依存関係を持つ項目をデータ キャッシュに追加できるようになりました。 この手順では、SQL キャッシュの依存関係を宣言的に操作する方法について説明します。 手順 6 では、プログラムによるアプローチについて説明します。
ObjectDataSource を使用したデータのキャッシュに関するチュートリアルでは、ObjectDataSource の宣言型キャッシュ機能について説明しました。
EnableCaching
プロパティを True
に設定し、CacheDuration
プロパティをある時間間隔に設定するだけで、ObjectDataSource は、指定した間隔の基になるオブジェクトから返されたデータを自動的にキャッシュします。 ObjectDataSource では、1 つ以上の SQL キャッシュ依存関係を使用することもできます。
SQL キャッシュの依存関係を宣言的に使用する方法を示すには、SqlCacheDependencies.aspx
フォルダーのCaching
ページを開き、ツールボックスからデザイナーに GridView をドラッグします。 GridView の ID
を ProductsDeclarative
に設定し、スマート タグから、 ProductsDataSourceDeclarative
という名前の新しい ObjectDataSource にバインドすることを選択します。
図 5: ProductsDataSourceDeclarative
という名前の新しい ObjectDataSource を作成する (フルサイズの画像を表示する をクリックします)
ProductsBLL
クラスを使用するように ObjectDataSource を構成し、[SELECT] タブのドロップダウン リストをGetProducts()
に設定します。 [UPDATE] タブで、UpdateProduct
、productName
、unitPrice
の 3 つの入力パラメーターを持つproductID
オーバーロードを選択します。 [挿入] タブと [DELETE] タブでドロップダウン リストを (なし) に設定します。
図 6: 3 つの入力パラメーターで UpdateProduct オーバーロードを使用する (フルサイズの画像を表示する をクリックします)
図 7: INSERT タブと DELETE タブの Drop-Down リストを (なし) に設定します (フルサイズの画像を表示する をクリックします)。
データ ソースの構成ウィザードが完了すると、Visual Studio によって各データ フィールドの BoundFields と CheckBoxFields が GridView に作成されます。
ProductName
、CategoryName
、UnitPrice
以外のすべてのフィールドを削除し、必要に応じてこれらのフィールドの書式を設定します。 GridView のスマート タグで、[ページングの有効化]、[並べ替えを有効にする]、および [編集を有効にする] チェック ボックスをオンにします。 Visual Studio によって、ObjectDataSource の OldValuesParameterFormatString
プロパティが original_{0}
に設定されます。 GridView の編集機能が正常に機能するためには、宣言構文からこのプロパティを完全に削除するか、既定値に戻すか、 {0}
。
最後に、GridView の上に Label Web コントロールを追加し、その ID
プロパティを ODSEvents
に設定し、その EnableViewState
プロパティを False
に設定します。 これらの変更を行った後、ページの宣言型マークアップは次のようになります。 SQL キャッシュの依存関係機能を示すために必要ではない GridView フィールドに対して、いくつかの美的なカスタマイズを行ったことに注意してください。
<asp:Label ID="ODSEvents" runat="server" EnableViewState="False" />
<asp:GridView ID="ProductsDeclarative" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceDeclarative"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="You must enter a valid currency value with
no currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" Display="Dynamic"
ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceDeclarative" runat="server"
SelectMethod="GetProducts" TypeName="ProductsBLL"
UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
次に、ObjectDataSource の Selecting
イベントのイベント ハンドラーを作成し、その中に次のコードを追加します。
Protected Sub ProductsDataSourceDeclarative_Selecting _
(sender As Object, e As ObjectDataSourceSelectingEventArgs) _
Handles ProductsDataSourceDeclarative.Selecting
ODSEvents.Text = "-- Selecting event fired"
End Sub
ObjectDataSource の Selecting
イベントは、基になるオブジェクトからデータを取得するときにのみ発生することを思い出してください。 ObjectDataSource が独自のキャッシュからデータにアクセスした場合、このイベントは発生しません。
次に、ブラウザーからこのページにアクセスします。 キャッシュをまだ実装していないため、グリッドのページング、並べ替え、または編集のたびに、"イベント選択が発生しました"というテキストが図8に示されるように、ページに表示されます。
図 8: ObjectDataSource の Selecting
イベントは、GridView がページング、編集、または並べ替えられるたびに発生します (フルサイズの画像を表示する をクリックします)。
ObjectDataSource を使用したデータのキャッシュに関するチュートリアルで説明したように、EnableCaching
プロパティを True
に設定すると、ObjectDataSource は、CacheDuration
プロパティで指定された期間、そのデータをキャッシュします。 ObjectDataSource には SqlCacheDependency
プロパティもあります。このプロパティは、次のパターンを使用して、キャッシュされたデータに 1 つ以上の SQL キャッシュ依存関係を追加します。
databaseName1:tableName1;databaseName2:tableName2;...
ここで、databaseName は、name
の<add>
要素のWeb.config
属性で指定されたデータベースの名前で、tableName はデータベース テーブルの名前です。 たとえば、Northwind の Products
テーブルに対する SQL キャッシュの依存関係に基づいて無期限にデータをキャッシュする ObjectDataSource を作成するには、ObjectDataSource の EnableCaching
プロパティを True
に設定し、その SqlCacheDependency
プロパティを NorthwindDB:Products に設定します。
注
SQL キャッシュの依存関係 と 時間ベースの有効期限を使用するには、 EnableCaching
を True
に設定し、時間間隔に CacheDuration
し、データベースとテーブル名に SqlCacheDependency
します。 ObjectDataSource は、時間ベースの有効期限に達したとき、またはポーリング システムが基になるデータベース データが変更されたことに注意した場合(どちらか早い方)、データを削除します。
SqlCacheDependencies.aspx
の GridView には、Products
とCategories
の 2 つのテーブルのデータが表示されます (製品のCategoryName
フィールドは、JOIN
のCategories
を使用して取得されます)。 したがって、2 つの SQL キャッシュ依存関係である NorthwindDB:Products を指定します。NorthwindDB:Categories .
図 9: Products
と Categories
の SQL キャッシュ依存関係を使用したキャッシュをサポートするように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)
キャッシュをサポートするように ObjectDataSource を構成した後、ブラウザーを使用してページを見直します。 ここでも、"イベント発生の選択"というテキストは最初にページを訪問した際に表示されますが、その後、ページング、並べ替え、または[編集]ボタンや[キャンセル]ボタンをクリックすると消える必要があります。 これは、データが ObjectDataSource のキャッシュに読み込まれた後、 Products
テーブルまたは Categories
テーブルが変更されるか、GridView を介してデータが更新されるまでそこに残るためです。
グリッドをページングし、[Selecting event fired text]\(イベントが発生したテキストの選択\) が表示されていないことに気が付いた後、新しいブラウザー ウィンドウを開き、[編集]、[挿入]、[削除] セクションの [基本] チュートリアル (~/EditInsertDelete/Basics.aspx
) に移動します。 製品の名前または価格を更新します。 次に、最初のブラウザー ウィンドウから別のページのデータを表示するか、グリッドを並べ替えるか、行の [編集] ボタンをクリックします。 今回は、基になるデータベース データが変更されたため、"発生したイベントの選択" が再び表示されます (図 10 を参照)。 テキストが表示されない場合は、しばらく待ってからやり直してください。 ポーリング サービスでは、Products
ミリ秒ごとにpollTime
テーブルへの変更がチェックされるため、基になるデータが更新されてからキャッシュされたデータが削除されるまでに遅延が発生します。
図 10: 製品テーブルを変更すると、キャッシュされた製品データが削除されます (フルサイズの画像を表示する をクリックします)。
手順 6: プログラムによるSqlCacheDependency
Class の操作
アーキテクチャのキャッシュ データチュートリアルでは、キャッシュと ObjectDataSource を密に結合するのではなく、アーキテクチャで別のキャッシュ レイヤーを使用する利点について説明しました。 このチュートリアルでは、データ キャッシュをプログラムで操作する方法を示す ProductsCL
クラスを作成しました。 キャッシュ層で SQL キャッシュの依存関係を利用するには、 SqlCacheDependency
クラスを使用します。
ポーリング システムでは、 SqlCacheDependency
オブジェクトを特定のデータベースとテーブルのペアに関連付ける必要があります。 たとえば、次のコードは、Northwind データベースの SqlCacheDependency
テーブルに基づいてProducts
オブジェクトを作成します。
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
SqlCacheDependency
のコンストラクターに対する 2 つの入力パラメーターは、それぞれデータベース名とテーブル名です。 ObjectDataSource の SqlCacheDependency
プロパティと同様に、使用されるデータベース名は、name
の<add>
要素のWeb.config
属性で指定された値と同じです。 テーブル名は、データベース テーブルの実際の名前です。
データ キャッシュに追加された項目に SqlCacheDependency
を関連付けるには、依存関係を受け入れる Insert
メソッド オーバーロードのいずれかを使用します。 次のコードは、データ キャッシュに無期限の期間の値を追加しますが、SqlCacheDependency
テーブルのProducts
に関連付けます。 つまり、メモリ制約が原因で削除されるまで、または テーブルがキャッシュされてから変更されたことがポーリング システムによって検出されるまで、Products
はキャッシュに残ります。
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Cache.Insert(key, _
value, _
productsTableDependency, _
System.Web.Caching.Cache.NoAbsoluteExpiration, _
System.Web.Caching.Cache.NoSlidingExpiration)
現在、Caching Layer の ProductsCL
クラスは、60 秒の時間ベースの有効期限を使用して、 Products
テーブルのデータをキャッシュします。 代わりに SQL キャッシュの依存関係を使用するように、このクラスを更新しましょう。 キャッシュにデータを追加する ProductsCL
クラスの AddCacheItem
メソッドには、現在、次のコードが含まれています。
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
' Add a CacheDependency
Dim dependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
DataCache.Insert(GetCacheKey(rawKey), value, dependency, _
DateTime.Now.AddSeconds(CacheDuration), _
Caching.Cache.NoSlidingExpiration)
End Sub
SqlCacheDependency
キャッシュの依存関係の代わりにMasterCacheKeyArray
オブジェクトを使用するように、このコードを更新します。
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Add the SqlCacheDependency objects for Products
Dim productsTableDependency As New _
Caching.SqlCacheDependency("NorthwindDB", "Products")
DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency, _
Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
この機能をテストするには、既存の ProductsDeclarative
GridView の下のページに GridView を追加します。 この新しい GridView の ID
を ProductsProgrammatic
に設定し、そのスマート タグを使用して、 ProductsDataSourceProgrammatic
という名前の新しい ObjectDataSource にバインドします。
ProductsCL
クラスを使用するように ObjectDataSource を構成し、SELECT タブと UPDATE タブのドロップダウン リストをそれぞれGetProducts
とUpdateProduct
に設定します。
図 11: ProductsCL
クラスを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)。
図 12: SELECT タブの Drop-Down リストから GetProducts
メソッドを選択します (フルサイズの画像を表示する をクリックします)。
図 13: Update タブの Drop-Down リストから UpdateProduct メソッドを選択します (フルサイズの画像を表示する をクリックします)。
データ ソースの構成ウィザードが完了すると、Visual Studio によって各データ フィールドの BoundFields と CheckBoxFields が GridView に作成されます。 このページに追加された最初の GridView と同様に、 ProductName
、 CategoryName
、 UnitPrice
を除くすべてのフィールドを削除し、必要に応じてこれらのフィールドの書式を設定します。 GridView のスマート タグで、[ページングの有効化]、[並べ替えを有効にする]、および [編集を有効にする] チェック ボックスをオンにします。
ProductsDataSourceDeclarative
ObjectDataSource と同様に、Visual Studio では、ProductsDataSourceProgrammatic
ObjectDataSource の OldValuesParameterFormatString
プロパティがoriginal_{0}
に設定されます。 GridView の編集機能が正常に機能するためには、このプロパティを {0}
に戻します (または、宣言構文からプロパティの割り当てを完全に削除します)。
これらのタスクを完了すると、結果として得られる GridView および ObjectDataSource 宣言型マークアップは次のようになります。
<asp:GridView ID="ProductsProgrammatic" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceProgrammatic" AllowPaging="True"
AllowSorting="True">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<EditItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>' />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName" Display="Dynamic"
ErrorMessage="You must provide a name for the product."
SetFocusOnError="True"
runat="server">*</asp:RequiredFieldValidator>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server"
Text='<%# Bind("ProductName") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
<EditItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice" Display="Dynamic"
ErrorMessage="You must enter a valid currency value with no
currency symbols. Also, the value must be greater than
or equal to zero."
Operator="GreaterThanEqual" SetFocusOnError="True"
Type="Currency" ValueToCompare="0">*</asp:CompareValidator>
</EditItemTemplate>
<ItemStyle HorizontalAlign="Right" />
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Bind("UnitPrice", "{0:c}") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSourceProgrammatic" runat="server"
OldValuesParameterFormatString="{0}" SelectMethod="GetProducts"
TypeName="ProductsCL" UpdateMethod="UpdateProduct">
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
</asp:ObjectDataSource>
キャッシュ層で SQL キャッシュの依存関係をテストするには、 ProductCL
クラスの AddCacheItem
メソッドにブレークポイントを設定し、デバッグを開始します。 最初にSqlCacheDependencies.aspx
にアクセスすると、データが初めて要求されてキャッシュに入るため、ブレークポイントで停止するはずです。 次に、GridView 内の別のページに移動するか、列の 1 つを並べ替えます。 これにより、GridView はデータの再クエリを実行しますが、 Products
データベース テーブルが変更されていないため、キャッシュ内にデータが見つかる必要があります。 データがキャッシュに繰り返し見つからない場合は、コンピューターに十分なメモリがあることを確認してから、もう一度やり直してください。
GridView のいくつかのページをページングした後、2 番目のブラウザー ウィンドウを開き、[編集]、[挿入]、[削除] セクションの [基本] チュートリアル (~/EditInsertDelete/Basics.aspx
) に移動します。 Products テーブルからレコードを更新し、最初のブラウザー ウィンドウで新しいページを表示するか、並べ替えヘッダーのいずれかをクリックします。
このシナリオでは、次の 2 つのいずれかが表示されます。いずれかのブレークポイントにヒットし、データベースの変更によりキャッシュされたデータが削除されたことを示します。または、ブレークポイントにヒットしません。つまり、 SqlCacheDependencies.aspx
に古いデータが表示されるようになりました。 ブレークポイントにヒットしない場合は、データが変更されてからポーリング サービスがまだ起動されていないことが原因である可能性があります。 ポーリング サービスでは、Products
ミリ秒ごとにpollTime
テーブルへの変更がチェックされるため、基になるデータが更新されてからキャッシュされたデータが削除されるまでに遅延が発生します。
注
この遅延は、 SqlCacheDependencies.aspx
の GridView を使用していずれかの製品を編集するときに表示される可能性が高くなります。 アーキテクチャチュートリアルのキャッシュ データでは、MasterCacheKeyArray
クラスの ProductsCL
メソッドを使用して編集されているデータがキャッシュから削除されるように、UpdateProduct
キャッシュの依存関係を追加しました。 ただし、この手順で AddCacheItem
メソッドを変更するときに、このキャッシュ依存関係を置き換えたので、ポーリング システムがProductsCL
テーブルへの変更をメモするまで、Products
クラスはキャッシュされたデータを表示し続けます。 手順 7 で、 MasterCacheKeyArray
キャッシュの依存関係を再導入する方法について説明します。
手順 7: キャッシュされた項目に複数の依存関係を関連付けます
MasterCacheKeyArray
キャッシュの依存関係は、その中に関連付けられている 1 つの項目が更新されたときに、すべての製品関連データがキャッシュから削除されるようにするために使用されることを思い出してください。 たとえば、GetProductsByCategoryID(categoryID)
メソッドは、一意の ProductsDataTables
値ごとにインスタンスキャッシュします。 これらのオブジェクトのいずれかが削除された場合、 MasterCacheKeyArray
キャッシュの依存関係により、他のオブジェクトも確実に削除されます。 このキャッシュ依存関係がないと、キャッシュされたデータが変更されると、他のキャッシュされた製品データが古くなっている可能性があります。 したがって、SQL キャッシュの依存関係を使用する場合は、 MasterCacheKeyArray
キャッシュの依存関係を維持することが重要です。 ただし、データ キャッシュの Insert
メソッドでは、1 つの依存関係オブジェクトのみが許可されます。
さらに、SQL キャッシュの依存関係を操作する場合は、複数のデータベース テーブルを依存関係として関連付ける必要がある場合があります。 たとえば、ProductsDataTable
クラスにキャッシュされたProductsCL
には各製品のカテゴリ名と仕入先名が含まれますが、AddCacheItem
メソッドではProducts
への依存関係のみが使用されます。 この状況では、ユーザーがカテゴリまたはサプライヤーの名前を更新した場合、キャッシュされた製品データはキャッシュに残り、古くなります。 したがって、キャッシュされた製品データは、 Products
テーブルだけでなく、 Categories
テーブルと Suppliers
テーブルにも依存する必要があります。
AggregateCacheDependency
クラスは、複数の依存関係をキャッシュ項目に関連付けるための手段を提供します。 まず、 AggregateCacheDependency
インスタンスを作成します。 次に、 AggregateCacheDependency
の Add
メソッドを使用して依存関係のセットを追加します。 その後、データ キャッシュに項目を挿入するときは、 AggregateCacheDependency
インスタンスを渡します。
インスタンスの依存関係AggregateCacheDependency
変更されると、キャッシュされた項目は削除されます。
ProductsCL
クラスの AddCacheItem
メソッドの更新されたコードを次に示します。 このメソッドは、MasterCacheKeyArray
、SqlCacheDependency
、およびProducts
テーブルのCategories
オブジェクトと共に、Suppliers
キャッシュの依存関係を作成します。 これらはすべて、AggregateCacheDependency
という名前の 1 つのaggregateDependencies
オブジェクトに結合され、Insert
メソッドに渡されます。
Private Sub AddCacheItem(ByVal rawKey As String, ByVal value As Object)
Dim DataCache As System.Web.Caching.Cache = HttpRuntime.Cache
' Make sure MasterCacheKeyArray(0) is in the cache - if not, add it.
If DataCache(MasterCacheKeyArray(0)) Is Nothing Then
DataCache(MasterCacheKeyArray(0)) = DateTime.Now
End If
'Create the CacheDependency
Dim masterCacheKeyDependency As _
New Caching.CacheDependency(Nothing, MasterCacheKeyArray)
' Add the SqlCacheDependency objects for Products, Categories, and Suppliers
Dim productsTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Products")
Dim categoriesTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Categories")
Dim suppliersTableDependency As _
New Caching.SqlCacheDependency("NorthwindDB", "Suppliers")
' Create an AggregateCacheDependency
Dim aggregateDependencies As New Caching.AggregateCacheDependency()
aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency, _
categoriesTableDependency, suppliersTableDependency)
DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies, _
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration)
End Sub
この新しいコードをテストします。これで、 Products
、 Categories
、または Suppliers
テーブルに変更が加えられると、キャッシュされたデータが削除されます。 さらに、 ProductsCL
クラスの UpdateProduct
メソッドは、GridView を使用して製品を編集するときに呼び出され、 MasterCacheKeyArray
キャッシュの依存関係を削除します。これにより、キャッシュされた ProductsDataTable
が削除され、次の要求でデータが再取得されます。
注
SQL キャッシュの依存関係は、 出力キャッシュと共に使用することもできます。 この機能のデモについては、「 SQL Server での ASP.NET 出力キャッシュの使用」を参照してください。
概要
データベース データをキャッシュする場合、データはデータベース内で変更されるまでキャッシュに残るのが理想的です。 ASP.NET 2.0 では、SQL キャッシュの依存関係を作成し、宣言型とプログラム型の両方のシナリオで使用できます。 このアプローチの課題の 1 つは、データがいつ変更されたかを検出することです。 Microsoft SQL Server 2005 の完全なバージョンでは、クエリ結果が変更されたときにアプリケーションに警告できる通知機能が提供されます。 SQL Server 2005 の Express Edition および以前のバージョンの SQL Server では、代わりにポーリング システムを使用する必要があります。 さいわい、必要なポーリング インフラストラクチャの設定は非常に簡単です。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。
- Microsoft SQL Server 2005 でのクエリ通知の使用
- クエリ通知の作成
-
SqlCacheDependency
クラスを使用した ASP.NET でのキャッシュ -
ASP.NET SQL Server 登録ツール (
aspnet_regsql.exe
) -
の概要
SqlCacheDependency
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Marko Rangel、テレサ マーフィー、ヒルトン ギセナウでした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。