Страницы

Поиск по вопросам

суббота, 9 марта 2019 г.

Обновление состояния редко изменяемых таблиц

Имеем алгоритм, который использует набор правил, который хранится в таблице MS SQL Server. Если переданные на вход параметры a,b,c совпали с хранящимися в таблице - будь добр, верни то, что хранится в поле d таблицы. Это для простоты, реально в базе в таблице RulesTree лежит отображение весьма раскидистого дерева правил, и каждый раз рекурсивно строить это дерево нет никакого желания - лишние запросы к БД, лишние расчеты et cetera. По грубым оценкам, с построением дерева алгоритм работает 5 секунд, без построения - 1 секунду, и это не на самой слабой системе.
Алгоритм реализуем в форме WCF-сервиса ValidationSerice. В static constructor сервиса вызываем метод pubic void RefreshCliassifiers(), который строит дерево правил, и сохраняет его в static List rulesList Работу с базой ведем через EF. Но правила имеют свойства меняться. Под это дело есть отдельный интерфейс, изменять который нельзя (т.е. мы не можем вызывать RefreshClassifiers или сохранять в базе итоговый набор правил в требуемом нашему алгоритму виде). Ну и классификатор, увы, не один. Меняются они редко, но меняются.
Вопросов два: 1. Нужно ли вообще в данном случае хранить дерево как static List rulesList, или есть другие варианты? 2. Как выяснить, что в таблицу RulesTree внесли изменения без перестроения списка правил?
Обновление: Попробовал через SqlDependency. Подготовил БД:
ALTER DATABASE [testbase] SET ENABLE_BROKER with rollback immediate;; CREATE QUEUE CntQueue; CREATE SERVICE CntChangeNotifications ON QUEUE CntQueue ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);
Проверяю следующим кодом и ловлю run-time exception: Если SqlDependency используется без указания значения для Оptions, то перед выполнением команды, добавленной в экземпляр SqlDependency, необходимо вызвать метод SqlDependency.Start()
private static void Main(string[] args) { var connString = "Server=(local);Database=testbase;User Id=developeruser;Password=developerpassword;"; SqlDependency.Stop(connString, "CntQueue"); var sqlCommand = new SqlCommand { CommandText = "select * from cnt", Notification = null, Connection = new SqlConnection(connString) }; SqlDependency dependency = new SqlDependency(sqlCommand); sqlCommand.Connection.Open(); SqlDependency.Start(connString, "CntQueue"); dependency.OnChange += OnChange; sqlCommand.ExecuteReader(); //!!!Exception!!!
while (!changed) { } Console.WriteLine("Changed!"); Console.ReadKey(); SqlDependency.Stop(connString, "CntQueue"); }
private static bool changed = false;
private static void OnChange(object sender, SqlNotificationEventArgs e) { changed = true; }
Но я же вызываю SqlDependency.Start()? Чего-то еще не хватает? exception.ToString():
System.InvalidOperationException: Если SqlDependency используется без указания значения для Оptions, то перед выполнением команды, добавленной в экземпляр SqlDependency, необходимо вызвать метод SqlDependency.Start(). в System.Data.SqlClient.SqlDependency.GetDefaultComposedOptions(String server, String failoverServer, IdentityUserNamePair identityUser, String database) в System.Data.SqlClient.SqlCommand.CheckNotificationStateAndAutoEnlist() в System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite) в System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) в System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) в System.Data.SqlClient.SqlCommand.ExecuteReader() в ConsoleApplication1.Program.Main(String[] args) в c:\csharp\projects\ConsoleApplication1\ConsoleApplication1\Program.cs:строка 33
UPD2. Заработал в итоге такой вариант:
SqlDependency dependency = new SqlDependency(sqlCommand, "Service=CntChangeNotifications;", 31557600);


Ответ

Следующая стратегия должна подойти:
Прочитать и закешировать в памяти дерево Подписаться на изменения таблиц при помощи SqlDependency При приходе нотификации инвалидировать кэш и перестроить его при следующем запросе к дереву.

Комментариев нет:

Отправить комментарий