2

SQLDependencyクラスでクエリ通知を調べています。簡単な実例を作成するのは簡単ですが、何かが足りないように感じます。テーブルが 1 つ、依存関係が 1 つの単純な例を過ぎて から、どの依存関係がコールバックをトリガーしたかをどのように特定できるのか疑問に思います。

ちょっと説明が難しいので、簡単な例を以下に示します。が呼び出されたときAChange()に、依存関係内の sql を見ることができず、関連するキャッシュ オブジェクトへの参照がありません。

それで、男の子は何をするのですか?

  • オプション 1 - 追跡したいオブジェクトごとに個別の関数を作成し、コールバックでキャッシュ キー (または関連情報) をハード コードします。これは汚いと感じ、新しいコードを展開せずに新しいキャッシュ項目を追加する可能性を排除します--ewww.
  • オプション 2 - DependencyIdプロパティと並行追跡構造を使用する

何か足りないだけですか?SQLDependencyこれは構造上の欠陥でしょうか?このトピックに関する 20 の異なる記事を見てきましたが、それらのすべてに同じ穴があるようです。提案?

コードサンプル

public class DependencyCache{
   public static  string       cacheName  = "Client1";
   public static  MemoryCache  memCache   = new MemoryCache(cacheName);

   public DependencyCache() {
      SqlDependency.Start(connString);
   }

   private static string GetSQL() {
      return "select  someString FROM dbo.TestTable";
   }

   public void DoTest() {
      if (memCache["TEST_KEY"] != null ) {
         Debug.WriteLine("resources found in cache");
         return;
      }
      Cache_GetData();
   }

   private void Cache_GetData() {
      SqlConnection         oConn;
      SqlCommand            oCmd;
      SqlDependency         oDep;
      SqlDataReader         oRS;
      List<string>          stuff    = new List<string>();
      CacheItemPolicy       policy   = new CacheItemPolicy();

      SqlDependency.Start(connString);
      using (oConn = new SqlConnection(connString) ) {
         using (oCmd = new SqlCommand(GetSQL(), oConn) ) {
            oDep = new SqlDependency(oCmd);
            oConn.Open();
            oRS = oCmd.ExecuteReader();

            while(oRS.Read() ) {
                  resources.Add( oRS.GetString(0) );
            }

            oDep.OnChange += new OnChangeEventHandler (AChange);
         }
      }
      memCache.Set("TEST_KEY", stuff, policy);
   }

   private void AChange(  object sender, SqlNotificationEventArgs e) {
      string msg= "Dependency Change \nINFO: {0} : SOURCE {1} :TYPE: {2}";
      Debug.WriteLine(String.Format(msg, e.Info, e.Source, e.Type));

      // If multiple queries use this as a callback how can i figure 
      // out WHAT QUERY TRIGGERED the change?
      // I can't figure out how to tell multiple dependency objects apart

      ((SqlDependency)sender).OnChange -= Cache_SqlDependency_OnChange; 
      Cache_GetData(); //reload data
   }
}
4

1 に答える 1

4

何よりもまず、コマンドを実行する前にハンドラーを設定する必要があります。

 oDep = new SqlDependency(oCmd);
 oConn.Open();
 oDep.OnChange += new OnChangeEventHandler (AChange);
 oRS = oCmd.ExecuteReader();
 while(oRS.Read() ) {
     resources.Add( oRS.GetString(0) );
 }

そうしないと、通知が失われ、コールバックが呼び出されない可能性があるウィンドウが表示されます。

質問について: クエリごとに個別のコールバックを使用する必要があります。これは面倒に思えるかもしれませんが、ラムダを使用することで実際には簡単です。次のようなもの:

oDep = new SqlDependency(oCmd);
oConn.Open();
oDep.OnChange += (sender, e) =>
{
   string msg = "Dependency Change \nINFO: {0} : SOURCE {1} :TYPE: {2}";
   Debug.WriteLine(String.Format(msg, e.Info, e.Source, e.Type));

   // The command that trigger the notification is captured in the context:
   //  is oCmd
   //
   // You can now call a handler passing in the relevant info:
   //
   Reload_Data(oCmd, ...);
};
oRS = oCmd.ExecuteReader();
...

また、通知のソース、情報、およびタイプを常に確認することを忘れないでください。そうしないと、無効なクエリなど、データ変更以外理由で通知されたときに、うんざりしてしまう危険があります。余談ですが、優れたキャッシュ設計は無効化時にキャッシュを更新するのではなく、単にキャッシュされたアイテムを無効化し、次のリクエストで実際に新しいアイテムを取得できるようにすることを付け加えておきます。あなたの「プロアクティブ」なアプローチでは、キャッシュされたアイテムを必要のないときでもリフレッシュしたり、アクセスする前に複数回リフレッシュしたりしています。エラー処理と適切なスレッド同期の例は省略しました (両方とも必要です)。

最後に、LinqtoCacheを見てください。これは、あなたがしようとしていることのほとんどを実行しますが、LINQ クエリ用です。

于 2012-05-22T10:16:52.690 に答える