Как отслеживать изменения таблицы SQL Server с помощью С#?

У меня есть несколько приложений, обращающихся к одной и той же БД, и мне нужно получить уведомление, если одно из этих приложений что-то изменит (обновить, вставить) в определенной таблице.

База данных и приложения не находятся на одном сервере.

Ответ 1

Вы можете использовать SqlDependency Class. Его предназначение используется в основном для страниц ASP.NET(небольшое количество уведомлений клиентов).

ALTER DATABASE UrDb SET ENABLE_BROKER

Внесите событие OnChange, чтобы получить уведомление:

void OnChange(object sender, SqlNotificationEventArgs e)

И в коде:

SqlCommand cmd = ...
cmd.Notification = null;

SqlDependency dependency = new SqlDependency(cmd);

dependency.OnChange += OnChange;

Он использует Service Broker (коммуникационную платформу на основе сообщений) для получения сообщений от механизма базы данных.

Ответ 2

В интересах полноты есть пара других решений, которые (на мой взгляд) являются более ортодоксальными, чем решения, основанные на классах SqlDependency (и SqlTableDependency). SqlDependency изначально была разработана для упрощения обновления кэшей распределенного веб-сервера и поэтому была построена в соответствии с другим набором требований, чем если бы она была разработана как производитель событий.

Существует в целом четыре варианта, некоторые из которых еще не были рассмотрены здесь:

  • Отслеживание изменений
  • CDC
  • Триггеры в очереди
  • CLR

Отслеживание изменений

Источник: https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server

Отслеживание изменений - это легкий механизм уведомлений на SQL-сервере. По сути, номер версии всей базы данных увеличивается с каждым изменением любых данных. Затем номер версии записывается в таблицы отслеживания изменений с битовой маской, включающей имена столбцов, которые были изменены. Обратите внимание, что фактическое изменение не сохраняется. Уведомление содержит только информацию о том, что конкретный объект данных изменился. Кроме того, поскольку управление версиями таблицы изменений является накопительным, уведомления об изменениях для отдельных элементов не сохраняются и перезаписываются более новыми уведомлениями. Это означает, что если сущность изменяется дважды, отслеживание изменений будет знать только о самых последних изменениях.

Чтобы зафиксировать эти изменения в c #, необходимо использовать опрос. Таблицы отслеживания изменений могут быть опрошены, и каждое изменение может быть проверено, чтобы увидеть, представляет ли он интерес. Если это представляет интерес, необходимо затем перейти непосредственно к данным, чтобы получить текущее состояние.

Сбор данных изменений

Источник: https://technet.microsoft.com/en-us/library/bb522489(v=sql.105).aspx

Сбор данных изменений (CDC) является более мощным, но наиболее дорогостоящим, чем отслеживание изменений. Сбор данных изменений будет отслеживать и уведомлять об изменениях на основе мониторинга журнала базы данных. Благодаря этому CDC имеет доступ к фактическим данным, которые были изменены, и ведет учет всех индивидуальных изменений.

Аналогично отслеживанию изменений, чтобы зафиксировать эти изменения в c #, необходимо использовать опрос. Однако в случае CDC информация об опросе будет содержать детали изменения, поэтому нет необходимости возвращаться к самим данным.

Триггеры в очереди

Источник: https://code.msdn.microsoft.com/Service-Broker-Message-e81c4316

Этот метод зависит от триггеров на таблицах, из которых требуются уведомления. Каждое изменение запускает триггер, и триггер записывает эту информацию в очередь компонента Service Broker. Затем к очереди можно подключиться через С# с помощью обработчика сообщений Service Broker (пример по ссылке выше).

В отличие от отслеживания изменений или CDC, триггеры в очереди не зависят от опроса и, таким образом, обеспечивают события в реальном времени.

CLR

Это техника, которую я видел, но я бы не рекомендовал ее. Любое решение, которое использует CLR для внешней связи, в лучшем случае является взломом. CLR был разработан, чтобы упростить написание сложного кода для обработки данных за счет использования С#. Он не был предназначен для подключения к внешним зависимостям, таким как библиотеки сообщений. Кроме того, связанные с CLR операции могут прерваться в кластеризованных средах непредсказуемым образом.

Тем не менее, это довольно просто настроить, так как все, что вам нужно сделать, это зарегистрировать сборку сообщений в CLR, а затем вы можете отозвать вызов с помощью триггеров или заданий SQL.

В итоге...

Она всегда была источником удивления мне, что Microsoft упорно отказывается решать эту проблему пространства. Событие из базы данных в код должно быть встроенной функцией продукта базы данных. Учитывая, что Oracle Advanced Queuing в сочетании с событием ODP.net MessageAvailable предоставили надежную базу данных для С# более 10 лет назад, это ужасно для MS.

В результате ни одно из решений, перечисленных в этом вопросе, не очень хорошее. Все они имеют технические недостатки и имеют значительную стоимость установки. Microsoft, если вы слушаете, пожалуйста, разберитесь с этим печальным положением дел.

Ответ 4

Используйте SqlTableDependency. Это компонент С#, вызывающий события при изменении записи. Другие подробности можно найти по адресу: https://github.com/christiandelbianco/monitor-table-change-with-sqltabledependency

Он аналогичен .NET SqlDependency, за исключением того, что SqlTableDependency вызывает события, содержащие измененные/удаленные или обновленные значения таблицы базы данных:

string conString = "data source=.;initial catalog=myDB;integrated security=True";

using(var tableDependency = new SqlTableDependency<Customers>(conString))
{
    tableDependency.OnChanged += TableDependency_Changed;
    tableDependency.Start();

    Console.WriteLine("Waiting for receiving notifications...");
    Console.WriteLine("Press a key to stop");
    Console.ReadKey();
}
...
...
void TableDependency_Changed(object sender, RecordChangedEventArgs<Customers> e)
{
    if (e.ChangeType != ChangeType.None)
    {
        var changedEntity = e.Entity;
        Console.WriteLine("DML operation: " + e.ChangeType);
        Console.WriteLine("ID: " + changedEntity.Id);
        Console.WriteLine("Name: " + changedEntity.Name);
        Console.WriteLine("Surname: " + changedEntity.Surname);
    }
}

Ответ 5

Будьте осторожны, используя класс SqlDependency - у него есть проблемы с утечками памяти.

Просто используйте кроссплатформенное решение, совместимое с .NET 3.5,.NET Core и с открытым исходным кодом - SqlDependencyEx. Вы можете получать уведомления, а также данные, которые были изменены (вы можете получить доступ к ним через свойства в объекте события уведомления). Вы также можете прикрепить операции DELETE\UPDATE\INSERT по отдельности или вместе.

Вот пример того, как легко использовать SqlDependencyEx:

int changesReceived = 0;
using (SqlDependencyEx sqlDependency = new SqlDependencyEx(
          TEST_CONNECTION_STRING, TEST_DATABASE_NAME, TEST_TABLE_NAME)) 
{
    sqlDependency.TableChanged += (o, e) => changesReceived++;
    sqlDependency.Start();

    // Make table changes.
    MakeTableInsertDeleteChanges(changesCount);

    // Wait a little bit to receive all changes.
    Thread.Sleep(1000);
}

Assert.AreEqual(changesCount, changesReceived);

Пожалуйста, перейдите по ссылкам для деталей. Этот компонент был протестирован во многих приложениях уровня предприятия и доказал свою надежность. Надеюсь это поможет.

Ответ 6

SqlDependency не наблюдает за базой данных, которую он наблюдает за SqlCommand, которую вы указываете, поэтому, если вы пытаетесь позволить вставить значения в базу данных в 1 проекте и захватить это событие в другом проекте, это не сработает, потому что это событие произошло из SqlCommand из проекта 1º, а не базы данных, потому что при создании SqlDependency вы связываете его с SqlCommand и только тогда, когда эта команда из этого проекта используется, создает событие изменения.

Ответ 8

похож на плохую архитектуру. также вы не указали тип приложения, которое необходимо уведомить (веб-приложение/консольное приложение /winforms/service и т.д.)

тем не менее, чтобы ответить на ваш вопрос, есть несколько способов решения этого. вы можете использовать:

1), если вы просто заинтересованы в том, чтобы следующий набор обновлений из второго приложения не противоречил обновлениям из первого приложения

2) объект зависимости sql - см. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx для получения дополнительной информации

3) пользовательская услуга push-уведомления, которую несколько клиентов (веб/winform/service) могут подписаться и получать уведомления об изменениях

Короче говоря, вам нужно использовать самое простое и простое и дешевое (с точки зрения усилий) решение, основанное на том, насколько сложны ваши требования к уведомлению и для какой цели вам нужно их использовать. не пытайтесь создать слишком сложную систему уведомлений, если ваши простые данные concurrency - это ваше единственное требование (в этом случае для простого решения на основе временной метки)

Ответ 9

Другим, очень простым способом мониторинга таблиц является управление версиями таблиц. Система доказала свою эффективность в таких конструкциях, как синхронизация DNS. Чтобы сделать это, вы создаете таблицу, содержащую имена таблиц и табличные версии, как decimal или bigint.. В каждой таблице, которую необходимо отслеживать, создайте триггер при вставке, обновлении и удалении, который будет увеличивать соответствующую таблицу в таблице версий при выполнении. Если вы ожидаете, что какая-либо из отслеживаемых таблиц будет часто изменена, вам необходимо предоставить повторное использование версии. Наконец, в вашем приложении каждый раз, когда вы запрашиваете контролируемую таблицу, вы также запрашиваете ее версию и сохраняете ее. Когда вы переходите на изменение контролируемой таблицы из своего приложения, вы сначала запрашиваете текущую версию и обрабатываете изменение только в том случае, если версия не изменяется. Вы можете хранить proc на сервере sql, чтобы это работало для вас. Это чрезвычайно простое, но проверенное твердое решение. Он имеет специфическое функциональное использование (для обеспечения согласованности данных) и освещает ресурсы (вы не поднимаете брокерские события, которые вы бы не наблюдали), но нуждаются в приложении, чтобы активно проверять изменения, а не пассивно ждать события.