Domanda

Sto esplorando le notifiche della query con la classe SQLDependency. Costruire un semplice esempio di lavoro è facile, ma mi sento come se mi manchi qualcosa. Una volta che ho passato un semplice esempio di un semplice tavolino / un esempio di dipendenza, sono lasciato a chiedersi come posso capire quale dipendenza ha attivato il mio callback?

Sto vivendo un po 'di problemi a spiegare, quindi ho incluso il semplice esempio qui sotto. Quando viene chiamato AChange(), non riesco a guardare lo SQL all'interno della dipendenza e non ho un riferimento all'oggetto cache associato.

Allora, cos'è un ragazzo da fare?

    .
  • Opzione 1 - Creare una funzione distinta per ciascun oggetto che voglio tracciare e il codice rigido del tasto cache (o informazioni pertinenti) nel callback. Questo si sente sporco ed elimina la possibilità di aggiungere nuovi elementi cache senza distribuire il nuovo codice - ewww.
  • Opzione 2 - Utilizzare la proprietà Id di dipendenza e una struttura di tracciamento parallelo

    Mi manca solo qualcosa? È una carenza nella struttura SQLDependency? Ho guardato 20 articoli diversi sull'argomento e tutti sembrano avere lo stesso buco. Suggerimenti?

    Codice campione

    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
       }
    }
    
    .

È stato utile?

Soluzione

Prima di tutto: il gestore deve essere impostato Prima Il comando viene eseguito:

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

Altrimenti hai una finestra quando la notifica potrebbe essere persa e la tua callback non ha mai invocato.

Ora sulla tua domanda: dovresti usare una richiamata separata per ogni query. Mentre questo può sembrare ingombrante, è in realtà banale usando una lambda. Qualcosa come quanto segue:

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

e ricordati di sempre Controllare la sorgente di notifica, le informazioni e il tipo. Altrimenti si esegue il rischio di rotazione ad-nauseam quando si nota per motivi altri rispetto alla modifica dei dati, come la query non valida. Come commento laterale, aggiungerei che un buon design della cache non aggiorna la cache sull'invalidazione, ma semplicemente invalida l'elemento memorizzato nella cache e consente a richiesta successiva recupera effettivamente un oggetto fresco. Con il tuo approccio "proattivo" si stanno rinfrescando gli articoli memorizzati nella cache anche quando non è necessario, aggiornare più volte prima di accedere, ecc. Ecc. Ecc. Ecc. Ho escluso dalla gestione degli errori di esempio e dalla corretta sincronizzazione del thread (entrambi richiesti).

Infine, dai un'occhiata a linqtocache che fa praticamente quello che stai cercando fare, ma per le query LINQ.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top