如何确定哪个SQLDependency触发了更改功能?

时间:2012-05-18 15:26:41

标签: sql-server ado.net sql-server-2008-r2 sqldependency query-notifications

我正在使用SQLDependency类探索查询通知。建立一个简单的工作示例很容易,但我觉得我错过了一些东西。一旦我走过一个简单的单表/单依赖示例,我就会想知道如何确定哪个依赖项触发了我的回调?

我在解释时遇到了一些麻烦,所以我在下面提供了一个简单的例子。调用AChange()时,我无法查看依赖项中的sql,并且我没有对相关缓存对象的引用。

那么男孩该做什么?

  • 选项1 - 为我想跟踪的每个对象创建一个不同的函数,并对回调中的缓存密钥(或相关信息)进行硬编码。这感觉很脏&消除了在不部署新代码的情况下添加新缓存项的可能性 - ewww。
  • 选项2 - 使用依赖关系Id属性和并行跟踪结构

我只是错过了什么?这是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
   }
}

1 个答案:

答案 0 :(得分:4)

首先:在执行命令之前,必须先设置处理程序:

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

否则,当通知可能丢失且您的回调从未被调用时,您会有一个窗口。

现在关于你的问题:你应该为每个查询使用单独的回调。虽然这看起来很麻烦,但使用lambda实际上是微不足道的。如下所示:

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();
...

请记住始终检查通知来源,信息和类型。否则,当您收到原因 other 而不是数据更改(如无效查询)时,您可能会面临转动恶心的风险。作为旁注,我想补充说好的缓存设计不会在失效时刷新缓存,而只是使缓存的项无效并让下一个请求实际获取一个新项。随着你的主动&#39;即使不需要,你也可以刷新缓存的项目,在访问之前刷新多次等等。我从示例错误处理和正确的线程同步(都需要)中遗漏了。

最后,看一下LinqtoCache,它几​​乎完成了你尝试做的事情,但是对于LINQ查询。