Страницы

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

воскресенье, 12 января 2020 г.

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

#c_sharp #entity_framework


Имеем алгоритм, который использует набор правил, который хранится в таблице 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);

    


Ответы

Ответ 1



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

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

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