Як контролювати зміни таблиці SQL Server за допомогою c #?


77

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

База даних та програми знаходяться не на одному сервері.


4
Яке повідомлення вам потрібно? Негайно? Вам потрібна програма, щоб отримати сповіщення, або вам потрібно надіслати електронний лист? Вам справді потрібно отримувати сповіщення, чи ви просто хочете відстежити ці зміни?
Річард

7
Можливо, ви захочете зупинитися наступного разу після того, як задасте питання, щоб ви могли пояснити / взаємодіяти з відповідачами. ТАК користувачі злісно відповідають на відповіді, і ви витрачаєте гарну нагоду отримати хороші відповіді, якщо не наводите курсор на ваше запитання, чекаючи відповіді.
Річард

Мені просто потрібно знати, чи будь-яке інше додаток оновлює чи вставляє будь-які дані, мені не потрібні самі дані, лише позначка, що ця таблиця має нові зміни. вибачте за запізнення, я не знав, що відповіді такі швидкі
ToDayIsNow

Відповіді:


55

Ви можете використовувати 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(комунікаційну платформу на основі повідомлень) для отримання повідомлень від механізму баз даних.


@jaroslav jandek, Привіт. Чи знаєте ви інший спосіб, окрім sqldependency? У мене проблема з sqldependency, оскільки він має багато обмежень, таких як OUTER JOIN; який я використовую майже у всіх своїх запитах sql!
M_Mogharrabi

@M_Mogharrabi Сповіщення виконуються з використанням індексації, яку не можна використовувати із зовнішніми об'єднаннями. Вам доведеться робити свої об’єднання вручну, а зовнішні об’єднання - як окремі запити. Я б намагався уникнути цього в більшості випадків.
Ярослав Яндек

2
@Kiquenet Performance SB тут не є проблемою. Однак сповіщення про запити можуть мати значний вплив на продуктивність БД. Особливо, якщо повідомлень багато (згадано в моїй відповіді). Якщо це так, вам може бути краще з опитуванням, SOA, ...
Ярослав Яндек

Я використовував SqlSependency для зміни бази даних тригера , щоб показати повідомлення поштовху до клієнта , але недавно ми переїхали в SQL Azure і воно не підтримує , SqlSependencyтак чи є кращий спосіб , ніж це , щоб отримувати повідомлення при зміні даних SQL Azure або коли нові дані вставляються?
Shaiju T

1
@stom заміни немає, AFAIK. Якщо ви контролюєте введення даних, ви можете легко повідомити про це за допомогою SignalR або подібних технологій ...
Ярослав Яндек

45

В інтересах повноти є ще кілька рішень, які (на мій погляд) є більш ортодоксальними, ніж рішення, що покладаються на класи 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

Цей прийом залежить від тригерів у таблицях, з яких вимагаються сповіщення. Кожна зміна запускатиме тригер, і тригер запише цю інформацію до черги брокера послуг. Потім чергу можна підключити через C # за допомогою процесора обміну повідомленнями брокера послуг (зразок за посиланням вище).

На відміну від відстеження змін або CDC, тригери до черг не покладаються на опитування і тим самим забезпечують події в реальному часі.

CLR

Це техніка, яку я бачив, використовувану, але я б не рекомендував її. Будь-яке рішення, яке покладається на CLR для зовнішнього спілкування, - це в кращому випадку хакерство. CLR був розроблений, щоб полегшити написання складного коду обробки даних, використовуючи C #. Він не був розроблений для підключення зовнішніх залежностей, таких як бібліотеки обміну повідомленнями. Крім того, операції, пов'язані з CLR, можуть непередбачувано розбиватися в кластерних середовищах.

Тим не менш, налаштування досить просто, оскільки все, що вам потрібно зробити, це зареєструвати збірку обміну повідомленнями в CLR, а потім ви можете зателефонувати за допомогою тригерів або завдань SQL.

Підсумовуючи ...

Мене завжди викликало подив для того, що Microsoft наполегливо відмовлялася вирішувати цю проблему. Перехід від бази даних до коду повинен бути вбудованою функцією продукту бази даних. Враховуючи, що Oracle Advanced Queuing у поєднанні з подією ODP.net MessageAvailable забезпечили надійну базу даних, що надходить до C # більше 10 років тому , це страшно від MS.

Результатом цього є те, що жодне з перелічених у цьому питанні рішень не є дуже приємним. Всі вони мають технічні недоліки та мають значну вартість налаштування. Корпорація Майкрософт, якщо ви слухаєте, будь ласка, розберіть цей жалюгідний стан справ.


17

Як правило, ви використовуєте брокер послуг

Тобто тригер -> черга -> програма (и)

Редагуйте, побачивши інші відповіді:

FYI: "Сповіщення про запит" побудовано на брокері послуг

Edit2:

Більше посилань


Я використовував SqlSependency для зміни бази даних тригера , щоб показати повідомлення поштовху до клієнта , але недавно ми переїхали в SQL Azure і воно не підтримує , SqlSependencyтак чи є кращий спосіб , ніж це , щоб отримувати повідомлення при зміні даних SQL Azure або коли нові дані вставляються?
Shaiju T

8

Використовуйте 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);
    }
}

7

Будьте обережні, використовуючи клас 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);

Будь ласка, перейдіть за посиланнями для отримання детальної інформації. Цей компонент був перевірений у багатьох прикладних програмах на корпоративному рівні та продемонстрував свою надійність. Сподіваюся, це допомагає.


2
Чи сумісний він із Sql Express?
dmigo

Звичайно, це сумісно
Дятченко

6

SqlDependency не переглядає базу даних, вона переглядає вказаний вами SqlCommand, тому, якщо ви намагаєтеся дозволити вкласти значення в базу даних у 1 проект і захопити цю подію в іншому проекті, це не буде працювати, оскільки подія була з SqlCommand з Проект 1º не є базою даних, тому що коли ви створюєте SqlDependency, ви пов'язуєте її з SqlCommand і лише тоді, коли використовується команда з цього проекту, він створює подію Change.


4
Це насправді не правильно. SqlDependency працює, навіть якщо ви вставляєте значення в Management Studio. Однак у цього класу є багато проблем, таких як витоки пам'яті. Дивіться мою відповідь нижче для деталей. @KayLee
dyatchenko

@dyatchenko, дякую за вашу думку. Я використовую SqlTableDependency, який згадувався в одній відповіді цього допису. Зараз я така зайнята, але, звичайно,
Кей Лі,


2

виглядає як погана архітектура на всьому шляху. також ви не вказали тип програми, про яку вам потрібно повідомити (веб-програма / консольна програма / winform / сервіс тощо тощо)

проте, щоб відповісти на ваше запитання, існує кілька способів вирішити це. Ви можете використовувати:

1) позначки часу, якщо вас просто цікавило забезпечення наступного набору оновлень з другої програми, не суперечить оновленням з першої програми

2) об'єкт залежності sql - див. Http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx для отримання додаткової інформації

3) спеціальна послуга push-сповіщень, на яку можуть підписатися та отримувати повідомлення про зміни декілька клієнтів (веб / winform / service)

коротше кажучи, вам потрібно використовувати найпростіше, найпростіше та найдешевше (з точки зору зусиль) рішення, виходячи зі того, наскільки складними є ваші вимоги до сповіщень і з якою метою їх потрібно використовувати. не намагайтеся побудувати надто складну систему сповіщень, якщо простою одночасністю даних є ваша єдина вимога (у такому випадку вибирайте просте рішення на основі мітки часу)


19
Просто з цікавості, не могли б ви пояснити, що тут таке "погана архітектура"?
Джеймс,

1

Іншим, дуже простим способом моніторингу таблиць є управління версіями таблиць. Доведено, що система працює в таких конструкціях, як синхронізація DNS. Щоб це працювало, ви створюєте таблицю, що містить імена та версії таблиць як decimalабоbigint.У кожній таблиці, яку потрібно контролювати, створіть тригер для вставки, оновлення та видалення, який збільшить відповідну версію таблиці в таблиці версій при виконанні. Якщо ви очікуєте, що будь-яка з відстежуваних таблиць буде часто змінюватися, вам потрібно забезпечити повторне використання версії. Нарешті, у вашій програмі, кожного разу, коли ви надсилаєте запит до відстежуваної таблиці, ви також запитуєте її версію та зберігаєте. Коли ви переходите до зміни відстежуваної таблиці з вашого додатка, ви спочатку ставите запит до його поточної версії та обробляєте зміни, лише якщо версія незмінна. Ви можете зберегти proc на сервері sql, який вам підходить. Це надзвичайно просте, але перевірене тверде рішення.


Я думаю, це створить вузьке місце для блокування навколо рядка, що містить версію для даної таблиці.
Dan Def

0

Це не зовсім сповіщення, але в заголовку ви кажете монітор, і це може відповідати цьому сценарію.

Використання стовпця часової позначки SQL Server дозволяє легко бачити будь-які зміни (які все ще зберігаються) між запитами.

На мій погляд, тип стовпця мітки часу SQL Server погано названий, оскільки він взагалі не пов’язаний з часом, це значення в масштабі бази даних, яке автоматично зростає при будь-якій вставці чи оновленні. Ви можете вибрати Макс. (Позначку часу) у таблиці, за якою ви шукаєте, або повернути позначку часу з рядка, який ви щойно вставили, а потім просто виберіть, де позначка часу> зберігаєтьсяМірка часу, це дасть вам усі результати, які були оновлені або вставлені між цими часами.

Оскільки це також значення для бази даних, ви можете використовувати свою збережену мітку часу, щоб перевірити, чи в будь-якій таблиці були записані дані з моменту останньої перевірки / оновлення збереженої мітки часу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.