Как выяснить, какая функция SQLDependency вызвала изменение?
-
11-12-2019 - |
Вопрос
Я изучаю уведомления о запросах с помощью 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.