Come capire quale SQLDependency ha attivato la funzione di modifica?
-
11-12-2019 - |
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 paralleloMi 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 } }
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.