web-dev-qa-db-ja.com

C#を使用してSQL Serverテーブルの変更を監視する方法は?

同じDBにアクセスする複数のアプリケーションがあり、これらのアプリケーションのいずれかが特定のテーブルで何かを変更(更新、挿入)した場合、通知を受け取る必要があります。

データベースとアプリは同じサーバーにありません。

61
ToDayIsNow

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(メッセージベースの通信プラットフォーム)。データベースエンジンからメッセージを受信します。

51
Jaroslav Jandek

完全を期すために、私の意見では、SqlDependency(およびSqlTableDependency)クラスに依存するソリューションよりも正統で定評のある他のソリューションがいくつかあります。 SqlDependencyは、Webサーバーのキャッシュ更新用に設計されているため、イベントプロデューサーに要求されるような負荷下での回復力を実際には提供しません。

ここにはまだ記載されていない他の4つのオプションがあります。

  • 変更追跡
  • CDC
  • キューへのトリガー
  • CLR

変更追跡

ソース: https://docs.Microsoft.com/en-us/sql/relational-databases/track-changes/about-change-tracking-sql-server

変更追跡は、SQLサーバーの軽量な通知メカニズムです。基本的に、データベース全体のバージョン番号は、データが変更されるたびに増加します。次に、バージョン番号は、変更された列の名前を含むビットマスクを使用して、変更追跡テーブルに書き込まれます。実際の変更は保持されないことに注意してください。通知には、特定のデータエンティティが変更されたという情報のみが含まれます。さらに、変更テーブルのバージョン管理は累積的であるため、個々のアイテムの変更通知は保持されず、新しい通知によって上書きされます。つまり、エンティティが2回変更された場合、変更の追跡では最新の変更のみが認識されます。

C#でこれらの変更をキャプチャするには、ポーリングを使用する必要があります。変更追跡テーブルをポーリングし、各変更を検査して関心があるかどうかを確認できます。興味がある場合は、データに直接移動して現在の状態を取得する必要があります。

Change Data Capture

ソース: 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メッセージプロセッサを使用してC#経由で接続できます(上記のリンクのサンプル)。

変更の追跡やCDCとは異なり、キューへのトリガーはポーリングに依存しないため、リアルタイムのイベントを提供します。

[〜#〜] clr [〜#〜]

これは私が使用しているのを見たテクニックですが、お勧めしません。 CLRに依存して外部と通信するソリューションは、せいぜいハックです。 CLRは、C#を活用して複雑なデータ処理コードを簡単に記述できるように設計されました。メッセージングライブラリなどの外部依存関係を配線するようには設計されていません。さらに、クラスター化された環境では、CLRにバインドされた操作が予期しない方法で中断する可能性があります。

これは、メッセージングアセンブリをCLRに登録するだけで、トリガーまたはSQLジョブを使用して呼び出すことができるため、セットアップは非常に簡単です。

まとめ...

マイクロソフトがこの問題空間に対処することを断固として拒否したことは、私にとって常に驚きの源でした。データベースからコードへのイベンティングは、データベース製品の組み込み機能である必要があります。 Oracle Advanced QueuingとODP.net MessageAvailable イベントが組み合わされて、10年以上前にC#に信頼できるデータベースイベンティングが提供されたことを考えると、これはMSからは悲惨です。

この結果は、この質問にリストされている解決策はどれも非常に素晴らしいものではないということです。それらはすべて技術的な欠点があり、セットアップに大きなコストがかかります。 Microsoftをお聞きになっている場合は、この残念な状況を整理してください。

30
tom redfern

通常、使用するのは Service Broker

それはトリガー->キュー->アプリケーションです

他の回答を見た後、編集します。

参考:「クエリ通知」はサービスブローカー上に構築されています

編集2:

その他のリンク

17
gbn

SqlTableDependencyを使用します。レコードが変更されたときにイベントを発生させるc#コンポーネントです。他の詳細は以下で見つけることができます: https://github.com/christiandelbianco/monitor-table-change-with-sqltabledependency

SqlTableDependencyは、変更/削除または更新されたデータベーステーブル値を含むイベントを発生させることを除いて、.NET SqlDependencyと同様です。

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);
    }
}

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
dyatchenko

SqlDependencyは指定されたSqlCommandを監視するデータベースを監視しないため、1つのプロジェクトでデータベースに値を挿入し、別のプロジェクトでそのイベントをキャプチャしようとすると、イベントはSqlCommandからのものであるため動作しません1ºプロジェクトはデータベースではなく、SqlDependencyを作成すると、それをSqlCommandにリンクし、そのプロジェクトからのコマンドが使用される場合にのみChangeイベントが作成されるためです。

6

SQL Server 2005以降では Query Notifications を使用するオプションがあり、ADO.NETで活用できます http://msdn.Microsoft.com/en-us/library/t9x04ed2を参照してください.aspx

4
Chris Taylor

ずっと悪いアーキテクチャのように見えます。また、通知する必要があるアプリの種類(Webアプリ/コンソールアプリ/ winforms /サービスなど)を指定していません

それにもかかわらず、あなたの質問に答えるために、これを解決する複数の方法があります。あなたが使用することができます:

1)2番目のアプリからの次の更新セットが最初のアプリからの更新と競合しないことを確認したいだけの場合のタイムスタンプ

2)sql依存オブジェクト-詳細については http://msdn.Microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx を参照してください

3)複数のクライアント(web/winform/service)がサブスクライブして変更に関する通知を受け取ることができるカスタムプッシュ通知サービス

要するに、通知要件がどれほど複雑で、どのような目的で使用する必要があるかに基づいて、最もシンプルで簡単で安価な(労力に関して)ソリューションを使用する必要があります。単純なデータの同時実行が唯一の要件である場合、過度に複雑な通知システムを構築しようとしないでください(その場合は、単純なタイムスタンプベースのソリューションに進みます)

2
Raj

テーブルを監視する別の非常に簡単な方法は、テーブルのバージョン管理です。このシステムは、DNS同期などの構築で機能することが実証されています。動作させるには、テーブル名とテーブルバージョンを含むテーブルをdecimalまたはbigint.として作成します。監視する必要のある各テーブルで、挿入、更新、および削除時にトリガーを作成します。実行時のテーブル。監視対象テーブルのいずれかが頻繁に変更されることが予想される場合は、バージョンの再利用をプロビジョニングする必要があります。最後に、アプリケーションでは、監視対象テーブルをクエリするたびに、そのバージョンもクエリして保存します。アプリから監視対象テーブルを変更する場合、まず現在のバージョンを照会し、バージョンが変更されていない場合にのみ変更を処理します。 SQL Serverにストアドプロシージャを使用して、その作業を行うことができます。これは非常にシンプルですが、実績のある堅牢なソリューションです。 (データの一貫性を確保するために)特定の機能的な用途があり、リソースが少ない(監視しないブローカーイベントを発生させない)が、イベントが発生するのを受動的に待つのではなく、アプリケーションがアクティブに変更を確認する必要があります。

1
ArtK