SQL キャッシュ依存関係を使用する (C#)
最も簡単なキャッシュ戦略は、キャッシュされたデータを指定した期間の後に期限切れにすることです。 ただし、この単純な方法は、キャッシュされたデータが基になるデータ ソースとの関連付けを維持しないため、古いデータの保持期間が長くなったり、現在のデータがすぐに期限切れになったりします。 より良い方法は、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 の通知機能に関するその他のリソースについては、このチュートリアルの最後にある「さらに読む」セクションをご覧ください。
ポーリングでは、データベースに 3 つの列 (tableName
、notificationCreated
、changeId
) を含む AspNet_SqlCacheTablesForChangeNotification
という名前のテーブルを含むように構成する必要があります。 このテーブルには、Web アプリケーションの SQL キャッシュ依存関係で使用する必要がある可能性があるデータを含む各テーブルの行が含まれています。 tableName
列はテーブルの名前を指定し、notificationCreated
は行がテーブルに追加された日時を示します。 changeId
列は int
型であり、初期値は 0 です。 その値は、テーブルに変更が加えられるたびにインクリメントされます。
データベースには、AspNet_SqlCacheTablesForChangeNotification
テーブルに加えて、SQL キャッシュの依存関係に現れる可能性がある各テーブルのトリガーも含める必要があります。 これらのトリガーは、行が挿入、更新、または削除されるたびに実行され、AspNet_SqlCacheTablesForChangeNotification
でテーブルの changeId
値がインクリメントされます。
ASP.NET ランタイムは、SqlCacheDependency
オブジェクトを使用してデータをキャッシュするときに、テーブルの現在の changeId
を追跡します。 データベースは定期的にチェックされ、changeId
がデータベース内の値と異なる SqlCacheDependency
オブジェクトは削除されます。これは、changeId
値が異なる場合、データがキャッシュされてからテーブルが変更されたことを示すためです。
手順 1: aspnet_regsql.exe
コマンド ライン プログラムを探索する
ポーリング手法では、前述のインフラストラクチャを含むデータベースをセットアップする必要があります (定義済みのテーブル (AspNet_SqlCacheTablesForChangeNotification
)、いくつかのストアド プロシージャ、および Web アプリケーションの SQL キャッシュ依存関係で使用できる各テーブルのトリガー)。 これらのテーブル、ストアド プロシージャ、およびトリガーは、$WINDOWS$\Microsoft.NET\Framework\version
フォルダーにあるコマンド ライン プログラム aspnet_regsql.exe
を使用して作成できます。 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
Note
これらのコマンドを実行するには、指定されたデータベース ログインが db_securityadmin
および db_ddladmin
ロールに含まれている必要があります。
たとえば、Windows 認証を使用する ScottsServer
という名前のデータベース サーバー上の pubs
という名前の 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
ScottsServer
の pubs
データベースの authors
テーブルと titles
テーブルにトリガーを追加するには、次のようにします。
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 データベースのデータベースとサーバー名が何か分かりますか? データベース名とサーバー名を検出する必要はなく、SQL Server Management Studio を使用してデータベースを localhost\SQLExpress
データベース インスタンスにアタッチし、データの名前を変更するのが最も簡単な方法であることがわかりました。 コンピューターに SQL Server 2005 の完全なバージョンのいずれかがインストールされている場合は、コンピューターに SQL Server Management Studio が既にインストールされている可能性があります。 Express エディションのみを使用している場合は、無料の Microsoft SQL Server Management Studio Express Edition をダウンロードできます。
まず、Visual Studio を閉じます。 次に、SQL Server Management Studio を開き、Windows 認証を使用して localhost\SQLExpress
サーバーに接続することを選択します。
図 1: localhost\SQLExpress
サーバーに接続する
サーバーに接続すると、Management Studio にサーバーが表示され、データベースやセキュリティなどのサブフォルダーが表示されます。 [データベース] フォルダーを右クリックし、[アタッチ] オプションを選択します。 [データベースのアタッチ] ダイアログ ボックスが表示されます (図 2 を参照)。 [追加] ボタンをクリックし、Web アプリケーションの App_Data
フォルダー内の NORTHWND.MDF
データベース フォルダーを選択します。
図 2: App_Data
フォルダーから NORTHWND.MDF
データベースをアタッチする (クリックするとフルサイズの画像が表示されます)
データベースが [データベース] フォルダーに追加されます。 データベース名は、データベース ファイルへの完全なパス、または GUID で始まる完全なパスである場合があります。 aspnet_regsql.exe コマンド ライン ツールを使用するときに、この長いデータベース名を入力しなくてもよいように、アタッチしたデータベースを右クリックし、[名前の変更] を選択して、データベースの名前をわかりやすい名前に変更します。 ここでは、データベースの名前を DataTutorials に変更しました。
図 3: アタッチされたデータベースの名前をよりわかりやすい名前に変更する
手順 3: Northwind データベースにポーリング インフラストラクチャを追加する
App_Data
フォルダーから NORTHWND.MDF
データベースをアタッチしたので、ポーリング インフラストラクチャを追加する準備ができました。 データベースの名前を 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>
<add>
要素 (NorthwindDB) の name
値は、人間が判読できる名前を特定のデータベースに関連付けます。 SQL キャッシュの依存関係を使用する場合は、ここで定義されているデータベース名と、キャッシュされたデータの基になるテーブルを参照する必要があります。 手順 6 では、SqlCacheDependency
クラスを使用して SQL キャッシュの依存関係をキャッシュされたデータにプログラムで関連付ける方法について説明します。
SQL キャッシュの依存関係が確立されると、ポーリング システムは pollTime
ミリ秒ごとに <databases>
要素で定義されているデータベースに接続し、AspNet_SqlCachePollingStoredProcedure
ストアド プロシージャを実行します。 このストアド プロシージャ (aspnet_regsql.exe
コマンド ライン ツールを使用して手順 3 で追加したもの) は、AspNet_SqlCacheTablesForChangeNotification
の各レコードの tableName
と changeId
値を返します。 古い SQL キャッシュの依存関係がキャッシュから削除されます。
pollTime
設定では、パフォーマンスとデータの陳腐化とのトレードオフが生じます。 pollTime
値を小さくすると、データベースへの要求の数が増えますが、キャッシュから古いデータがより迅速に削除されます。 pollTime
値を大きくするとデータベース要求の数が減りますが、バックエンド データが変更されてから、関連するキャッシュ項目が削除されるまでの遅延が長くなります。 幸いなことに、データベース要求では、単純な軽量テーブルから数行のみを返す単純なストアド プロシージャが実行されています。 ただし、さまざまな pollTime
値を試して、アプリケーションのデータベース アクセスとデータの陳腐化との間の理想的なバランスを見つけてください。 使用できる pollTime
の最小値は 500 です。
Note
上記の例では、<sqlCacheDependency>
要素に 1 つの pollTime
値を指定していますが、必要に応じて pollTime
要素に <add>
値を指定できます。 これは、複数のデータベースを指定し、データベースごとのポーリング頻度をカスタマイズする場合に便利です。
手順 5: SQL キャッシュの依存関係を宣言的に操作する
手順 1 から 4 では、必要なデータベース インフラストラクチャをセットアップし、ポーリング システムを構成する方法について説明しました。 このインフラストラクチャが整ったので、プログラムまたは宣言型の手法を使用して、関連する SQL キャッシュの依存関係を持つ項目をデータ キャッシュに追加できるようになりました。 この手順では、SQL キャッシュの依存関係を宣言的に操作する方法について説明します。 手順 6 では、プログラムによる手法について説明します。
ObjectDataSource を使用してデータをキャッシュするチュートリアルでは、ObjectDataSource の宣言型キャッシュ機能について説明しました。 EnableCaching
プロパティを true
に、CacheDuration
プロパティをある時間間隔に設定するだけで、ObjectDataSource は指定された間隔で基になるオブジェクトから返されたデータを自動的にキャッシュします。 ObjectDataSource では、1 つ以上の SQL キャッシュ依存関係を使用することもできます。
SQL キャッシュの依存関係を宣言的に使用する方法を示すには、Caching
フォルダー内の SqlCacheDependencies.aspx
ページを開き、ツールボックスからデザイナーに GridView をドラッグします。 GridView の ID
を ProductsDeclarative
に設定し、スマート タグから ProductsDataSourceDeclarative
という名前の新しい ObjectDataSource にバインドすることを選択します。
図 5: ProductsDataSourceDeclarative
という名前の新しい ObjectDataSource を作成します (クリックするとフルサイズの画像が表示されます)
ProductsBLL
クラスを使用するように ObjectDataSource を構成し、[SELECT] タブのドロップダウン リストを GetProducts()
に設定します。 [UPDATE] タブで、3 つの入力パラメーター (productName
、unitPrice
、productID
) を含む UpdateProduct
オーバーロードを選択します。 [INSERT] タブと [DELETE] タブでドロップダウン リストを (なし) に設定します。
図 6: 3 つの入力パラメーターを含む UpdateProduct オーバーロードを使用する (クリックするとフルサイズの画像が表示されます)
図 7: INSERT タブと DELETE タブのドロップダウン リストを (なし) に設定する (クリックするとフルサイズの画像が表示されます)
データ ソースの構成ウィザードが完了すると、Visual Studio によって各データ フィールドの BoundField と CheckBoxField が GridView に作成されます。 ProductName
、CategoryName
、UnitPrice
を除くすべてのフィールドを削除し、これらのフィールドを必要に応じて書式設定します。 GridView のスマート タグで、[ページングを有効にする]、[並べ替えを有効にする]、[編集を有効にする] チェック ボックスをオンにします。 Visual Studio では、ObjectDataSource の OldValuesParameterFormatString
プロパティが original_{0}
に設定されます。 GridView の編集機能が正常に動作するためには、宣言構文からこのプロパティを完全に削除するか、既定値 ({0}
) に戻します。
最後に、GridView の上に Label Web コントロールを追加し、その ID
プロパティを ODSEvents
に、EnableViewState
プロパティを false
に設定します。 これらの変更を行った後、ページの宣言型マークアップは次のようになります。 GridView フィールドに対して、SQL キャッシュの依存関係機能を示すために必要ではないいくつかの美的なカスタマイズを行っていることに注意してください。
<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 void ProductsDataSourceDeclarative_Selecting
(object sender, ObjectDataSourceSelectingEventArgs e)
{
ODSEvents.Text = "-- Selecting event fired";
}
ObjectDataSource の Selecting
イベントは、基になるオブジェクトからデータを取得するときにのみ発生することを思い出してください。 ObjectDataSource が独自のキャッシュからデータにアクセスした場合、このイベントは発生しません。
次に、ブラウザーからこのページにアクセスします。 キャッシュはまだ実装していないため、グリッドをページング、並べ替え、または編集するたびに、図 8 に示すように、"Selecting event fired" というテキストがページに表示されます。
図 8: ObjectDataSource の Selecting
イベントは、GridView のページング、編集、または並べ替えが行われるたびに発生します (クリックするとフルサイズの画像が表示されます)
ObjectDataSource を使用してデータをキャッシュするチュートリアルで説明したように、EnableCaching
プロパティを true
に設定すると、ObjectDataSource は CacheDuration
プロパティで指定された期間、そのデータをキャッシュします。 ObjectDataSource には SqlCacheDependency
プロパティもあり、このパターンを使用してキャッシュされたデータに 1 つ以上の SQL キャッシュの依存関係を追加します。
databaseName1:tableName1;databaseName2:tableName2;...
ここで databaseName は Web.config
で <add>
要素の name
属性に指定されたデータベースの名前、tableName はデータベース テーブルの名前です。 たとえば、Northwind の Products
テーブルに対する SQL キャッシュの依存関係に基づいて無期限にデータをキャッシュする ObjectDataSource を作成するには、ObjectDataSource の EnableCaching
プロパティを true
に、SqlCacheDependency
プロパティを NorthwindDB:Products に設定します。
Note
SQL キャッシュの依存関係、および時間ベースの有効期限を使用するには、EnableCaching
を true
に、CacheDuration
を時間間隔に、SqlCacheDependency
をデータベースとテーブル名に設定します。 ObjectDataSource は、時間ベースの有効期限に達したとき、またはポーリング システムが基になるデータベース データが変更されたことに気づいた場合にデータを削除します (どちらか早い方)。
SqlCacheDependencies.aspx
の GridView には、2 つのテーブル (Products
と Categories
) からのデータが表示されます (製品の CategoryName
フィールドは Categories
の JOIN
を介して取得されます)。 したがって、2 つの SQL キャッシュ依存関係である NorthwindDB:Products;NorthwindDB:Categories を指定します。
図 9: Products
および Categories
で SQL キャッシュの依存関係を使用したキャッシュをサポートするように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)
キャッシュをサポートするように ObjectDataSource を構成した後、ブラウザーを使用してページに再度アクセスします。 ここでも、"Selecting event fired" というテキストがページに最初にアクセスした際に表示されますが、ページング、並べ替え、あるいは [編集] ボタンまたは [キャンセル] ボタンをクリックすると消えます。 これは、ObjectDataSource のキャッシュにデータが読み込まれた後、Products
または Categories
テーブルが変更されるか、GridView を使用してデータが更新されるまで、データがそこに残るためです。
グリッドをページングし、"Selecting event fired" というテキストが表示されていないことを確認した後、新しいブラウザー ウィンドウを開き、[編集]、[挿入]、[削除] セクション (~/EditInsertDelete/Basics.aspx
) の [基本] チュートリアルに移動します。 製品の名前または価格を更新します。 最初のブラウザー ウィンドウから、データの別のページを表示したり、グリッドを並べ替えたり、行の [編集] ボタンをクリックしたりします。 今回は、基になるデータベース データが変更されたため、"Selecting event fired" が再び表示されます (図 10 を参照)。 テキストが表示されない場合は、しばらく待ってからやり直してください。 ポーリング サービスでは、Products
テーブルの変更が pollTime
ミリ秒ごとにチェックされるため、基になるデータが更新されてからキャッシュされたデータが削除されるまでに遅延が発生します。
図 10: 製品テーブルを変更すると、キャッシュされた製品データが削除されます (クリックするとフルサイズの画像が表示されます)
手順 6: SqlCacheDependency
クラスをプログラムで操作する
アーキテクチャでデータをキャッシュするチュートリアルでは、キャッシュと ObjectDataSource を密に結合するのではなく、アーキテクチャで別のキャッシュ レイヤーを使用する利点について説明しました。 そのチュートリアルでは、データ キャッシュをプログラムで操作する方法を示す ProductsCL
クラスを作成しました。 キャッシュ レイヤーで SQL キャッシュの依存関係を利用するには、SqlCacheDependency
クラスを使用します。
ポーリング システムでは、SqlCacheDependency
オブジェクトを特定のデータベースとテーブルのペアに関連付ける必要があります。 たとえば、次のコードでは、Northwind データベースの Products
テーブルに基づいて SqlCacheDependency
オブジェクトを作成します。
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
SqlCacheDependency
のコンストラクターに対する 2 つの入力パラメーターは、それぞれデータベース名とテーブル名です。 ObjectDataSource の SqlCacheDependency
プロパティと同様に、使用されるデータベース名は、Web.config
で <add>
要素の name
属性で指定された値と同じです。 テーブル名は、データベース テーブルの実際の名前です。
SqlCacheDependency
をデータ キャッシュに追加された項目に関連付けるには、依存関係を受け入れる Insert
メソッド オーバーロードのいずれかを使用します。 次のコードは、無期限でデータ キャッシュに "値" を追加しますが、Products
テーブル上の SqlCacheDependency
と関連付けます。 要するに、"値" は、メモリ制約のために削除されるまで、または Products
テーブルがキャッシュされてから変更されたことがポーリング システムによって検出されるまでキャッシュに残ります。
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
Cache.Insert(key,
value,
productsTableDependency,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration);
現在、キャッシュ レイヤーの ProductsCL
クラスは、60 秒の時間ベースの有効期限を使用して Products
テーブルのデータをキャッシュしています。 代わりに SQL キャッシュの依存関係を使用するように、このクラスを更新しましょう。 キャッシュにデータを追加する ProductsCL
クラスの AddCacheItem
メソッドには、現在、次のコードが含まれています。
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Make sure MasterCacheKeyArray[0] is in the cache
DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
// Add a CacheDependency
Caching.CacheDependency dependency =
new Caching.CacheDependency(null, MasterCacheKeyArray);
DataCache.Insert(GetCacheKey(rawKey), value, dependency,
DateTime.Now.AddSeconds(CacheDuration),
System.Web.Caching.Cache.NoSlidingExpiration);
}
MasterCacheKeyArray
キャッシュの依存関係の代わりに SqlCacheDependency
オブジェクトを使用するように、このコードを更新します。
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Add the SqlCacheDependency objects for Products
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
// Add the item to the data cache using productsTableDependency
DataCache.Insert(GetCacheKey(rawKey), value, productsTableDependency,
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}
この機能をテストするには、既存 ProductsDeclarative
GridView の下のページに GridView を追加します。 スマート タグから、この新しい GridView の ID
を ProductsProgrammatic
に設定し、ProductsDataSourceProgrammatic
という名前の新しい ObjectDataSource にバインドします。 ProductsCL
クラスを使用するように ObjectDataSource を構成し、[SELECT] タブと [UPDATE] タブのドロップダウン リストをそれぞれ GetProducts
と UpdateProduct
に設定します。
図 11: ProductsCL
クラスを使用するように ObjectDataSource を構成する (クリックするとフルサイズの画像が表示されます)
図 12: [SELECT] タブのドロップダウン リストから GetProducts
メソッドを選択する (クリックするとフルサイズの画像が表示されます)
図 13: [UPDATE] タブのドロップダウン リストから UpdateProduct メソッドを選択する (クリックするとフルサイズの画像が表示されます)
データ ソースの構成ウィザードが完了すると、Visual Studio によって各データ フィールドの BoundField と CheckBoxField が 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
ミリ秒ごとにチェックされるため、基になるデータが更新されてからキャッシュされたデータが削除されるまでに遅延が発生します。
Note
この遅延は、SqlCacheDependencies.aspx
で GridView を使用していずれかの製品を編集するときに見られる可能性が高くなります。 アーキテクチャでデータをキャッシュするチュートリアルでは、ProductsCL
クラスの UpdateProduct
メソッドを使用して編集されているデータがキャッシュから削除されるように、MasterCacheKeyArray
キャッシュの依存関係を追加しました。 ただし、この手順の前に AddCacheItem
メソッドを変更するときに、このキャッシュ依存関係を置き換えたため、ProductsCL
クラスは、ポーリング システムが Products
テーブルへの変更に気が付くまで、キャッシュされたデータを表示し続けます。 手順 7 で MasterCacheKeyArray
キャッシュの依存関係を再導入する方法について説明します。
手順 7: キャッシュされた項目に複数の依存関係を関連付ける
MasterCacheKeyArray
キャッシュの依存関係は、その中に関連付けられている 1 つの項目が更新されたときに、すべての製品関連データがキャッシュから削除されるようにするために使用されることを思い出してください。 たとえば、GetProductsByCategoryID(categoryID)
メソッドは一意の categoryID 値ごとに ProductsDataTables
インスタンスをキャッシュします。 これらのオブジェクトのいずれかが削除された場合、MasterCacheKeyArray
キャッシュの依存関係により、他のオブジェクトも確実に削除されます。 このキャッシュ依存関係がないと、キャッシュされたデータが変更されたときに、他のキャッシュされた製品データが古くなっている可能性があります。 したがって、SQL キャッシュの依存関係を使用する場合は、MasterCacheKeyArray
キャッシュの依存関係を維持することが重要です。 ただし、データ キャッシュの Insert
メソッドでは、1 つの依存関係オブジェクトのみが許可されます。
さらに、SQL キャッシュの依存関係を操作する場合は、複数のデータベース テーブルを依存関係として関連付ける必要がある場合があります。 たとえば、ProductsCL
クラスにキャッシュされた ProductsDataTable
には各製品のカテゴリ名とサプライヤー名が含まれていますが、AddCacheItem
メソッドでは Products
への依存関係のみを使用します。 この状況では、ユーザーがカテゴリまたはサプライヤーの名前を更新した場合、キャッシュされた製品データはキャッシュに残り、古くなります。 そのため、キャッシュされた製品データを Products
テーブルだけでなく、Categories
テーブルや Suppliers
テーブルにも依存させる必要があります。
AggregateCacheDependency
クラスは、複数の依存関係をキャッシュ項目に関連付けるための手段を提供します。 まず、AggregateCacheDependency
インスタンスを作成します。 次に、AggregateCacheDependency
の Add
メソッドを使用して依存関係のセットを追加します。 その後、データ キャッシュに項目を挿入するときに、AggregateCacheDependency
インスタンスを渡します。 AggregateCacheDependency
インスタンスの依存関係のいずれかが変更されると、キャッシュされた項目は削除されます。
ProductsCL
クラスの AddCacheItem
メソッドの更新されたコードを次に示します。 このメソッドでは、MasterCacheKeyArray
キャッシュの依存関係が、Products
、Categories
、Suppliers
テーブルの SqlCacheDependency
オブジェクトと共に作成されます。 これらはすべて、Insert
メソッドに渡される aggregateDependencies
という名前の 1 つの AggregateCacheDependency
オブジェクトに結合されます。
private void AddCacheItem(string rawKey, object value)
{
System.Web.Caching.Cache DataCache = HttpRuntime.Cache;
// Make sure MasterCacheKeyArray[0] is in the cache and create a depedency
DataCache[MasterCacheKeyArray[0]] = DateTime.Now;
Caching.CacheDependency masterCacheKeyDependency =
new Caching.CacheDependency(null, MasterCacheKeyArray);
// Add the SqlCacheDependency objects for Products, Categories, and Suppliers
Caching.SqlCacheDependency productsTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Products");
Caching.SqlCacheDependency categoriesTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Categories");
Caching.SqlCacheDependency suppliersTableDependency =
new Caching.SqlCacheDependency("NorthwindDB", "Suppliers");
// Create an AggregateCacheDependency
Caching.AggregateCacheDependency aggregateDependencies =
new Caching.AggregateCacheDependency();
aggregateDependencies.Add(masterCacheKeyDependency, productsTableDependency,
categoriesTableDependency, suppliersTableDependency);
DataCache.Insert(GetCacheKey(rawKey), value, aggregateDependencies,
Caching.Cache.NoAbsoluteExpiration, Caching.Cache.NoSlidingExpiration);
}
この新しいコードをテストします。これで、Products
、Categories
、または Suppliers
テーブルに変更を加えると、キャッシュされたデータが削除されます。 さらに、GridView を使用して製品を編集するときに呼び出される ProductsCL
クラスの UpdateProduct
メソッドは、MasterCacheKeyArray
キャッシュの依存関係を削除します。これにより、キャッシュされた ProductsDataTable
が削除され、次の要求でデータが再取得されます。
Note
SQL キャッシュの依存関係は、出力キャッシュと共に使用することもできます。 この機能のデモについては、「SQL Server で ASP.NET 出力キャッシュを使用する」をご覧ください。
まとめ
データベース データをキャッシュする場合、データはデータベース内で変更されるまでキャッシュに残るのが理想的です。 ASP.NET 2.0 では、SQL キャッシュの依存関係を作成し、宣言型とプログラム型の両方のシナリオで使用できます。 このアプローチの課題の 1 つは、データがいつ変更されたかを検出することです。 Microsoft SQL Server 2005 の完全なバージョンでは、クエリ結果が変更されたときにアプリケーションに警告できる通知機能が提供されます。 SQL Server 2005 の Expressエディションおよび以前のバージョンの 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 は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Marko Rangel、Teresa Murphy、Hilton Giesenow でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。