Share via


SQL キャッシュ依存関係を使用する (VB)

作成者: Scott Mitchell

PDF のダウンロード

最も簡単なキャッシュ方法は、キャッシュされたデータが指定された期間後に期限切れになることです。 ただし、この単純な方法は、キャッシュされたデータが基になるデータ ソースとの関連付けを維持せず、古いデータが保持されすぎたり、有効期限が切れすぎた現在のデータが期限切れになったりすることを意味します。 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 の通知機能に関するその他のリソースについては、このチュートリアルの最後にある「その他の読み取り」セクションを参照してください。

ポーリングでは、、、および changeIdの 3 つの列notificationCreatedtableNameを持つ という名前AspNet_SqlCacheTablesForChangeNotificationのテーブルを含むようにデータベースを構成する必要があります。 このテーブルには、Web アプリケーションの SQL キャッシュ依存関係で使用する必要がある可能性があるデータを含む各テーブルの行が含まれています。 列は tableName テーブルの名前を指定し、 notificationCreated 行がテーブルに追加された日時を示します。 列の changeIdint は で、初期値は 0 です。 その値は、テーブルを変更するたびにインクリメントされます。

データベースには、テーブルに AspNet_SqlCacheTablesForChangeNotification 加えて、SQL キャッシュの依存関係に含まれる可能性がある各テーブルにトリガーを含める必要もあります。 これらのトリガーは、行が挿入、更新、または削除されるたびに実行され、 のAspNet_SqlCacheTablesForChangeNotificationテーブルのchangeId値がインクリメントされます。

ASP.NET ランタイムは、オブジェクトを使用してデータをキャッシュするときに、テーブルの現在 changeIdSqlCacheDependency を追跡します。 データベースが定期的にチェックされ、 SqlCacheDependency データベース内の値と異なるオブジェクト changeId は削除されます。値が異なる 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

注意

これらのコマンドを実行するには、指定されたデータベース ログインが ロールと db_ddladmin ロールにdb_securityadmin含まれている必要があります。

たとえば、Windows 認証を使用して という名前のデータベース サーバー上のという名前pubsScottsServerの 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

のデータベースの テーブルと titles テーブルにauthorsトリガーを追加するには、次の値をpubsScottsServer使用します。

aspnet_regsql.exe -S ScottsServer -E -d pubs -t authors -et
aspnet_regsql.exe -S ScottsServer -E -d pubs -t titles -et

このチュートリアルでは、および の各テーブルにProductsCategoriesトリガーを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使用してサーバーに接続することを選択します。

localhost\SQLExpress Server にアタッチする

図 1: サーバーにアタッチするlocalhost\SQLExpress

サーバーに接続すると、Management Studio にサーバーが表示され、データベースやセキュリティなどのサブフォルダーが表示されます。 [データベース] フォルダーを右クリックし、[アタッチ] オプションを選択します。 これにより、[データベースのアタッチ] ダイアログ ボックスが表示されます (図 2 を参照)。 [追加] ボタンをクリックし、Web アプリケーションの NORTHWND.MDF フォルダー内の App_Data データベース フォルダーを選択します。

NORTHWND をアタッチします。App_Data フォルダーからの MDF データベース

図 2: フォルダーからデータベースをNORTHWND.MDFApp_Dataアタッチする (クリックするとフルサイズの画像が表示されます)

これにより、データベースが [データベース] フォルダーに追加されます。 データベース名は、データベース ファイルへの完全パス、または GUID で付加された完全パスである可能性があります。 aspnet_regsql.exe コマンド ライン ツールを使用するときに、この長いデータベース名を入力する必要がないようにするには、アタッチしたデータベースを右クリックして [名前の変更] を選択して、データベースの名前をわかりやすい名前に変更します。 データベースの名前を DataTutorials に変更しました。

アタッチされたデータベースの名前を [その他の Human-Friendly 名] に変更する

図 3: アタッチされたデータベースの名前を Human-Friendly 名に変更する

手順 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)、新しいストアド プロシージャ、および 、Categories、および Suppliers テーブルのトリガーにProducts注意してください。

データベースに必要なポーリング インフラストラクチャが含まれるようになりました

図 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>

要素 ( NorthwindDB ) の<add>値はname、人間が判読できる名前を特定のデータベースに関連付けます。 SQL キャッシュの依存関係を操作する場合は、ここで定義されているデータベース名と、キャッシュされたデータの基になるテーブルを参照する必要があります。 クラスを使用 SqlCacheDependency して、SQL キャッシュの依存関係をキャッシュされたデータにプログラムで関連付ける方法については、手順 6 で説明します。

SQL キャッシュの依存関係が確立されると、ポーリング システムは要素で <databases> 定義されているデータベースにミリ秒ごとに pollTime 接続し、ストアド プロシージャを AspNet_SqlCachePollingStoredProcedure 実行します。 コマンド ライン ツールを使用してaspnet_regsql.exe手順 3 で追加されたこのストアド プロシージャは、 のAspNet_SqlCacheTablesForChangeNotification各レコードの と changeId の値を返しますtableName。 古い SQL キャッシュの依存関係は、キャッシュから削除されます。

この設定では pollTime 、パフォーマンスとデータの古さの間にトレードオフが生まれます。 小さい pollTime 値を指定すると、データベースに対する要求の数が増えますが、古いデータをより迅速にキャッシュから削除します。 値を大きく pollTime すると、データベース要求の数は減りますが、バックエンド データが変更されたときと、関連するキャッシュ項目が削除されるまでの遅延が増加します。 幸いなことに、データベース要求では、単純で軽量なテーブルから数行のみを返す単純なストアド プロシージャが実行されます。 ただし、さまざまな pollTime 値を試して、アプリケーションのデータベース アクセスとデータの古さの理想的なバランスを見つけます。 使用できる最小値 pollTime は 500 です。

注意

上記の例では、 要素に 1 つのpollTime値が<sqlCacheDependency>提供されていますが、必要に応じて 要素に値をpollTime<add>指定できます。 これは、複数のデータベースを指定し、データベースごとのポーリング頻度をカスタマイズする場合に便利です。

手順 5: SQL キャッシュの依存関係を宣言的に操作する

手順 1 から 4 では、必要なデータベース インフラストラクチャを設定し、ポーリング システムを構成する方法を確認しました。 このインフラストラクチャを配置することで、プログラムまたは宣言型の手法を使用して、関連付けられた SQL キャッシュ依存関係を持つ項目をデータ キャッシュに追加できるようになりました。 この手順では、SQL キャッシュの依存関係を宣言的に操作する方法について説明します。 手順 6 では、プログラムによるアプローチについて説明します。

ObjectDataSource を使用したデータのキャッシュに関するチュートリアルでは、ObjectDataSource の宣言型キャッシュ機能について説明しました。 プロパティを EnableCachingTrue 設定し、 プロパティを CacheDuration ある時間間隔に設定するだけで、ObjectDataSource は指定された間隔で基になるオブジェクトから返されたデータを自動的にキャッシュします。 ObjectDataSource では、1 つ以上の SQL キャッシュ依存関係を使用することもできます。

SQL キャッシュの依存関係を宣言的に使用する方法を示すには、フォルダー内のSqlCacheDependencies.aspxページをCaching開き、ツールボックスから Designer に GridView をドラッグします。 GridView を IDProductsDeclarative 設定し、そのスマート タグから、 という名前 ProductsDataSourceDeclarativeの新しい ObjectDataSource にバインドすることを選択します。

ProductsDataSourceDeclarative という名前の新しい ObjectDataSource を作成する

図 5: 名前付きの ProductsDataSourceDeclarative 新しい ObjectDataSource を作成する (クリックするとフルサイズの画像が表示されます)

クラスを使用するように ObjectDataSource を ProductsBLL 構成し、SELECT タブのドロップダウン リストを に GetProducts()設定します。 [UPDATE] タブで、および の UpdateProduct 3 つの入力パラメーターproductNameunitPriceを持つオーバーロードをproductID選択します。 [挿入] タブと [DELETE] タブでドロップダウン リストを [(なし)] に設定します。

3 つの入力パラメーターで UpdateProduct オーバーロードを使用する

図 6: 3 つの入力パラメーターで UpdateProduct オーバーロードを使用する (クリックするとフルサイズの画像が表示されます)

INSERT タブと DELETE タブの Drop-Down リストを (なし) に設定します

図 7: INSERT タブと DELETE タブの [Drop-Down リスト] を [(なし)] に設定します (フルサイズの画像を表示するには、ここをクリックします)

データ ソースの構成ウィザードが完了すると、Visual Studio は各データ フィールドの GridView に BoundFields と CheckBoxFields を作成します。 、CategoryName、および を除くProductNameすべてのフィールドをUnitPrice削除し、必要に応じてこれらのフィールドの書式を設定します。 GridView のスマート タグから、[ページングの有効化]、[並べ替えを有効にする]、[編集を有効にする] チェック ボックスをチェックします。 Visual Studio では、ObjectDataSource の プロパティが OldValuesParameterFormatStringoriginal_{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 に示すように)。

ObjectDataSource の Selecting イベントは、GridView が Paged、Edited、または Sorted のたびに発生します

図 8: ObjectDataSource の Selecting イベントは、GridView が Paged、Edited、または Sorted のたびに発生します (フルサイズの画像を表示するには、ここをクリックします)

ObjectDataSource を使用したデータのキャッシュに関するチュートリアルで説明したように、 プロパティを EnableCachingTrue設定すると、ObjectDataSource はそのプロパティでCacheDuration指定された期間、そのデータをキャッシュします。 ObjectDataSource には プロパティもあります。これにより、 パターンをSqlCacheDependency使用して、キャッシュされたデータに 1 つ以上の SQL キャッシュ依存関係が追加されます。

databaseName1:tableName1;databaseName2:tableName2;...

ここで 、databaseName は の 要素の属性で name 指定されたデータベースの <add> 名前であり Web.configtableName はデータベース テーブルの名前です。 たとえば、Northwind テーブルに対する SQL キャッシュの依存関係に基づいてデータを無期限にキャッシュする ObjectDataSource を作成するには、ObjectDataSource の ProductsEnableCaching プロパティを に True 設定し、その SqlCacheDependency プロパティを NorthwindDB:Products に設定します。

注意

SQL キャッシュの依存関係時間ベースの有効期限を使用するには、 を にTrueCacheDuration、時間間隔に、データベース名とSqlCacheDependencyテーブル名を に設定EnableCachingします。 ObjectDataSource は、時間ベースの有効期限に達したとき、または基になるデータベース データが変更されたことをポーリング システムがメモしたときに、データを削除します。どちらか早い方が発生します。

の GridView にはSqlCacheDependencies.aspx、 と の 2 つのテーブル ProductsCategories のデータが表示されます (製品のCategoryNameフィールドは をCategories介してJOIN取得されます)。 したがって、次の 2 つの SQL キャッシュ依存関係を指定します。NorthwindDB:Products;NorthwindDB:Categories .

製品とカテゴリに対する SQL キャッシュ依存関係を使用したキャッシュをサポートするように ObjectDataSource を構成する

図 9: と の SQL キャッシュ依存関係 ProductsCategories を使用したキャッシュをサポートするように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)

キャッシュをサポートするように ObjectDataSource を構成した後、ブラウザーを使用してページを見直します。 ここでも、"発生したイベントの選択" というテキストは最初のページの訪問時に表示されますが、[編集] ボタンまたは [キャンセル] ボタンをページング、並べ替え、またはクリックすると削除されます。 これは、データが ObjectDataSource のキャッシュに読み込まれた後、テーブルまたは Categories テーブルが変更されるか、GridView を介してデータが更新されるまでProducts、そのデータはそこに残るためです。

グリッドをページングし、"Selecting event fired text"の欠如に気が付いた後、新しいブラウザー ウィンドウを開き、[編集]、[挿入]、[削除] セクション (~/EditInsertDelete/Basics.aspx) の [基本] チュートリアルに移動します。 製品の名前または価格を更新します。 次に、最初のブラウザー ウィンドウに移動し、別のページのデータを表示するか、グリッドを並べ替えるか、行の [編集] ボタンをクリックします。 今回は、基になるデータベース データが変更されたため、"発生した選択イベント" が再び表示されます (図 10 を参照)。 テキストが表示されない場合は、しばらく待ってからやり直してください。 ポーリング サービスでは、テーブルに対する Products 変更がミリ秒ごとに pollTime チェックされるため、基になるデータが更新されるまでと、キャッシュされたデータが削除されるまでに遅延が発生します。

Products テーブルを変更して、キャッシュされた製品データを削除する

図 10: 製品テーブルを変更してキャッシュされた製品データを削除する (フルサイズの画像を表示する をクリックします)

手順 6: プログラムによるクラスのSqlCacheDependency操作

「アーキテクチャでのデータのキャッシュ」チュートリアルでは、キャッシュと ObjectDataSource を密に結合するのではなく、アーキテクチャで別のキャッシュ レイヤーを使用する利点について説明しました。 このチュートリアルでは、データ キャッシュを ProductsCL プログラムで操作する方法を示すクラスを作成しました。 キャッシュ層で SQL キャッシュの依存関係を利用するには、 クラスを使用します SqlCacheDependency

ポーリング システムでは、オブジェクトを特定の SqlCacheDependency データベースとテーブルのペアに関連付ける必要があります。 たとえば、次のコードでは、Northwind データベースのテーブルに基づいて オブジェクトをProducts作成SqlCacheDependencyします。

Dim productsTableDependency As _
    New Caching.SqlCacheDependency("NorthwindDB", "Products")

s コンストラクターへの SqlCacheDependency 2 つの入力パラメーターは、それぞれデータベース名とテーブル名です。 ObjectDataSource の SqlCacheDependency プロパティと同様に、使用されるデータベース名は、 の 要素Web.config<add> 属性でname指定された値と同じです。 テーブル名は、データベース テーブルの実際の名前です。

をデータ キャッシュに追加された項目に関連付けるには SqlCacheDependency 、依存関係を Insert 受け入れるメソッド オーバーロードのいずれかを使用します。 次のコードは、データ キャッシュに無期限のを追加しますが、テーブルの Products に関連付けますSqlCacheDependency。 つまり、 は、メモリ制約が原因で削除されるまで、またはテーブルがキャッシュされてから変更されたこと Products がポーリング システムによって検出されるまでキャッシュに残ります。

Dim productsTableDependency As _
    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キャッシュにデータを追加するクラス s 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 を IDProductsProgrammatic 設定し、そのスマート タグを使用して、 という名前 ProductsDataSourceProgrammaticの新しい ObjectDataSource にバインドします。 クラスを使用ProductsCLするように ObjectDataSource を構成し、SELECT タブと UPDATE タブのドロップダウン リストをそれぞれ と UpdateProductGetProducts設定します。

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

図 11: クラスを使用するように ObjectDataSource を構成する ProductsCL (クリックするとフルサイズの画像が表示されます)

SELECT タブの Drop-Down リストから GetProducts メソッドを選択します

図 12: SELECT タブの Drop-Down リストからメソッドを選択 GetProducts します (フルサイズの画像を表示する をクリックします)

UPDATE タブの Drop-Down リストから UpdateProduct メソッドを選択する

図 13: UPDATE タブの Drop-Down リストから UpdateProduct メソッドを選択します (フルサイズの画像を表示する をクリックします)

データ ソースの構成ウィザードが完了すると、Visual Studio は各データ フィールドの GridView に BoundFields と CheckBoxFields を作成します。 このページに最初の GridView を追加した場合と同様に、、CategoryName、、 UnitPrice以外のすべてのフィールドProductNameを削除し、必要に応じてこれらのフィールドの書式を設定します。 GridView のスマート タグから、[ページングの有効化]、[並べ替えを有効にする]、[編集を有効にする] チェック ボックスをチェックします。 ObjectDataSource と同様にProductsDataSourceDeclarative、Visual Studio は ObjectDataSource の OldValuesParameterFormatString プロパティを ProductsDataSourceProgrammaticoriginal_{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 キャッシュの依存関係をテストするには、 クラスの AddCacheItem メソッドにブレークポイントをProductCL設定し、デバッグを開始します。 に初めてアクセス SqlCacheDependencies.aspxすると、データが初めて要求され、キャッシュに配置されるため、ブレークポイントにヒットする必要があります。 次に、GridView 内の別のページに移動するか、列の 1 つを並べ替えます。 これにより、GridView はデータの再クエリを実行しますが、データベース テーブルが変更されていないため、データはキャッシュ Products 内に存在する必要があります。 キャッシュにデータが繰り返し見つからない場合は、コンピューターに十分なメモリがあることを確認してから、もう一度やり直してください。

GridView のいくつかのページをページングした後、2 番目のブラウザー ウィンドウを開き、[編集]、[挿入]、[削除] セクション (~/EditInsertDelete/Basics.aspx) の [基本] チュートリアルに移動します。 Products テーブルからレコードを更新し、最初のブラウザー ウィンドウで新しいページを表示するか、並べ替えヘッダーのいずれかをクリックします。

このシナリオでは、次の 2 つのいずれかが表示されます。いずれかのブレークポイントがヒットし、データベースの変更によりキャッシュされたデータが削除されたことを示します。または、ブレークポイントがヒットしません。つまり SqlCacheDependencies.aspx 、古いデータが表示されるようになりました。 ブレークポイントがヒットしない場合は、データが変更されてからポーリング サービスがまだ起動されていないためである可能性があります。 ポーリング サービスでは、テーブルの Products 変更がミリ秒ごとに pollTime チェックされるため、基になるデータが更新されるまでと、キャッシュされたデータが削除されるまでに遅延が発生します。

注意

この遅延は、 で GridView SqlCacheDependencies.aspxを使用していずれかの製品を編集するときに表示される可能性が高くなります。 アーキテクチャでのデータのキャッシュに関するチュートリアルでは、キャッシュの依存関係をMasterCacheKeyArray追加して、 クラスの UpdateProduct メソッドを使用してProductsCL編集されているデータがキャッシュから削除されたことを確認しました。 ただし、この手順でメソッドを変更するときにこのキャッシュ依存関係を AddCacheItem 置き換えたため ProductsCL 、ポーリング システムがテーブルへの変更をメモするまで、クラスはキャッシュされたデータを Products 引き続き表示します。 手順 7 でキャッシュの依存関係を再導入する MasterCacheKeyArray 方法について説明します。

手順 7: キャッシュされた項目に複数の依存関係を関連付ける

キャッシュの MasterCacheKeyArray 依存関係は、その中に関連付けられている 1 つの項目が更新されたときに、 すべての 製品関連データがキャッシュから削除されるようにするために使用されることを思い出してください。 たとえば、 メソッドは GetProductsByCategoryID(categoryID) 、一意の ProductsDataTablescategoryID 値ごとにインスタンスをキャッシュします。 これらのオブジェクトのいずれかが削除された場合、キャッシュの MasterCacheKeyArray 依存関係により、他のオブジェクトも確実に削除されます。 このキャッシュの依存関係がない場合、キャッシュされたデータが変更されると、他のキャッシュされた製品データが古くなっている可能性があります。 そのため、SQL キャッシュの依存関係を使用する場合は、キャッシュの依存関係を MasterCacheKeyArray 維持することが重要です。 ただし、データ キャッシュの メソッド Insert では、1 つの依存関係オブジェクトのみが許可されます。

さらに、SQL キャッシュの依存関係を使用する場合は、複数のデータベース テーブルを依存関係として関連付ける必要がある場合があります。 たとえば、 ProductsDataTable クラスに ProductsCL キャッシュされた には各製品のカテゴリ名と仕入先名が含まれていますが AddCacheItem 、 メソッドでは にのみ への Products依存関係が使用されます。 この状況では、ユーザーがカテゴリまたはサプライヤーの名前を更新した場合、キャッシュされた製品データはキャッシュに残り、古くなります。 そのため、キャッシュされた製品データをテーブルだけでなくProducts、 テーブルと Suppliers テーブルCategoriesにも依存させる必要があります。

クラスはAggregateCacheDependency、複数の依存関係をキャッシュ項目に関連付ける手段を提供します。 まず、インスタンスを AggregateCacheDependency 作成します。 次に、 メソッドを使用して依存関係のセットをAggregateCacheDependencyAdd追加します。 その後、データ キャッシュに項目を挿入する場合は、 インスタンスを AggregateCacheDependency 渡します。 インスタンスの依存関係のいずれかがAggregateCacheDependency変更されると、キャッシュされた項目が削除されます。

クラス s AddCacheItem メソッドの更新されたコードを次にProductsCL示します。 メソッドは、、、および テーブルのMasterCacheKeyArrayオブジェクトとSqlCacheDependency共にCategoriesProductsキャッシュの依存関係をSuppliers作成します。 これらはすべて という名前aggregateDependenciesの 1 つのAggregateCacheDependencyオブジェクトに結合され、 メソッドに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

この新しいコードをテストします。、、または Suppliers テーブルにCategoriesProducts変更を加えると、キャッシュされたデータが削除されます。 さらに、GridView を ProductsCL 使用して製品を編集するときに呼び出されるクラス s UpdateProduct メソッドは、キャッシュの依存関係を削除 MasterCacheKeyArray します。これにより、キャッシュ ProductsDataTable が削除され、次の要求でデータが再取得されます。

注意

SQL キャッシュの依存関係は、 出力キャッシュでも使用できます。 この機能のデモについては、「SQL Serverでの ASP.NET 出力キャッシュの使用」を参照してください。

まとめ

データベース データをキャッシュする場合、データはデータベースで変更されるまでキャッシュに残るのが理想的です。 ASP.NET 2.0 では、SQL キャッシュの依存関係を作成し、宣言型とプログラム型の両方のシナリオで使用できます。 このアプローチの課題の 1 つは、データがいつ変更されたかを検出することです。 Microsoft SQL Server 2005 の完全なバージョンには、クエリ結果が変更されたときにアプリケーションに警告できる通知機能が用意されています。 SQL Server 2005 以前のバージョンの SQL Server の Express Edition では、代わりにポーリング システムを使用する必要があります。 さいわい、必要なポーリング インフラストラクチャの設定は非常に簡単です。

プログラミングに満足!

もっと読む

このチュートリアルで説明するトピックの詳細については、次のリソースを参照してください。

著者について

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

特別な感謝

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