Pregunta

Estoy explorando notificaciones de consultas con el SQLDependency clase.Crear un ejemplo funcional sencillo es fácil, pero siento que me falta algo.Una vez que paso por un ejemplo simple de una tabla/una dependencia, me quedo preguntándome ¿Cómo puedo saber qué dependencia provocó mi devolución de llamada?

Me cuesta un poco explicarlo, así que incluí el ejemplo simple a continuación.Cuando AChange() se llama. No puedo ver el SQL dentro de la dependencia y no tengo una referencia al objeto de caché asociado.

Entonces, ¿qué debe hacer un niño?

  • Opción 1 - crear una función distinta para cada objeto que quiero rastrear y codificar la clave de caché (o información relevante) en la devolución de llamada.Esto se siente sucio y elimina la posibilidad de agregar nuevos elementos de caché sin implementar código nuevo: ewww.
  • opcion 2 - Utilice la dependencia Id propiedad y una estructura de seguimiento paralela

¿Me estoy perdiendo algo?¿Es esto una deficiencia en la SQLDependency ¿estructura?He mirado 20 artículos diferentes sobre el tema y todos parecen tener el mismo agujero.¿Sugerencias?

Ejemplo de código

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
   }
}
¿Fue útil?

Solución

Primero y ante todo:el controlador tiene que estar configurado antes se ejecuta el comando:

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

De lo contrario, tendrá una ventana en la que la notificación puede perderse y su devolución de llamada nunca se invocará.

Ahora sobre tu pregunta:debe utilizar una devolución de llamada separada para cada consulta.Si bien esto puede parecer engorroso, en realidad es trivial usando una lambda.Algo como lo siguiente:

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

Y recuerda siempre verifique la fuente, la información y el tipo de notificación.De lo contrario, corre el riesgo de dar vueltas hasta las náuseas cuando se le notifique por motivos otro que el cambio de datos, como una consulta no válida.Como comentario adicional, agregaría que un buen diseño de caché no actualiza el caché en caso de invalidación, sino que simplemente invalida el elemento almacenado en caché y permite que el próxima solicitud en realidad buscar un artículo nuevo.Con su enfoque "proactivo", está actualizando los elementos almacenados en caché incluso cuando no los necesita, actualiza varias veces antes de acceder a ellos, etc., etc.Dejé fuera del ejemplo el manejo de errores y la sincronización adecuada de subprocesos (ambos obligatorios).

Finalmente, echa un vistazo a LinqtoCache que hace prácticamente lo que estás intentando hacer, pero para consultas LINQ.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top