Вопрос

Я изучаю уведомления о запросах с помощью SQLDependency сорт.Создать простой рабочий пример легко, но мне кажется, что я чего-то упускаю.Как только я пройду мимо простого примера с одной таблицей и одной зависимостью, мне остается задаться вопросом: как мне выяснить, какая зависимость вызвала мой обратный вызов?

У меня возникли проблемы с объяснением, поэтому я привел простой пример ниже.Когда AChange() называется, я не могу просмотреть sql внутри зависимости, и у меня нет ссылки на связанный объект кэша.

Так что же делать мальчику?

  • Опция 1 — создать отдельную функцию для каждого объекта, который я хочу отслеживать, и жестко закодировать ключ кэша (или соответствующую информацию) в обратном вызове.Это кажется грязным и исключает возможность добавления новых элементов кэша без развертывания нового кода — фууу.
  • Вариант 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
   }
}
Это было полезно?

Решение

Прежде всего:обработчик должен быть настроен до команда выполняется:

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

И не забудьте всегда проверьте источник уведомления, информацию и тип.В противном случае вы рискуете накрутить рекламу, когда вас уведомят о причинах. другой чем изменение данных, например неверный запрос.В качестве дополнительного комментария я бы добавил, что хорошая конструкция кэша не обновляет кэш при аннулировании, а просто делает недействительным кэшированный элемент и позволяет следующий запрос на самом деле принести свежий товар.Благодаря вашему «проактивному» подходу вы обновляете кэшированные элементы, даже если они не нужны, обновляете несколько раз, прежде чем к ним будет осуществлен доступ и т. д. и т. п.Я исключил из примера обработку ошибок и правильную синхронизацию потоков (оба обязательны).

Наконец, взгляните на ЛинктоКэш который делает почти то же, что вы пытаетесь сделать, но для запросов LINQ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top