如何使用C#监视SQL Server表更改?


76

我有多个应用程序正在访问同一数据库,并且如果这些应用程序之一更改了某个表中的任何内容(更新,插入),我需要得到通知。

数据库和应用程序不在同一服务器上。


4
您需要哪种通知?即时?您是否需要通知应用程序,或者需要发送电子邮件给您?您是否真的需要得到通知,还是只想跟踪这些更改?
理查德

7
提出问题后,您可能想在下一次停留,以便进行澄清/与答卷者互动。这样,用户很快就会回答问题,并且如果您没有“将鼠标悬停在问题上”等待答复,那么您正在浪费一个获得良好答案的好机会。
理查德

我只需要知道是否有其他应用程序更新或插入了任何数据,就不需要数据本身只是该表具有新更改的标志。抱歉,我迟到了,我不知道答案是那样的快
ToDayIsNow 2011年

Answers:


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通知是使用无法与外部联接一起使用的索引完成的。您必须将外部联接作为单独的查询手动进行联接。在大多数情况下,我都会尽量避免这种情况。
Jaroslav Jandek

2
SB的@Kiquenet性能在这里不是问题。但是,查询通知可能会对数据库性能产生重大影响。特别是如果有很多通知(在我的回答中提到)。如果是这种情况,最好使用轮询,SOA,...
雅罗斯拉夫扬德克

我已经使用SqlSependency触发数据库更改以向客户端显示推送通知,但是最近我们移至SQL Azure,它不支持,SqlSependency所以有没有比这更好的方法来在SQL Azure数据更改或插入新数据时获取通知?
shaijut

1
@stom没有替代品,AFAIK。如果您控制数据输入,则可以使用SignalR或类似技术轻松地通知...
Jaroslav Jandek

45

为了完整性起见,还有其他一些解决方案(在我看来)比依赖SqlDependency(和SqlTableDependency)类的解决方案更为传统。SqlDependency最初旨在使刷新分布式Web服务器缓存更加容易,因此,与被设计为事件生产者相比,它是根据一组不同的要求构建的。

大致有四个选项,其中一些尚未在此处讨论:

  • 变更追踪
  • CDC
  • 触发队列
  • CLR

变更追踪

资源: https //docs.microsoft.com/zh-cn/sql/relational-databases/track-changes/about-change-tracking-sql-server

更改跟踪是SQL Server中的一种轻量级通知机制。基本上,数据库范围内的版本号会随着对任何数据的每次更改而增加。然后使用位掩码(包括已更改列的名称)将版本号写入更改跟踪表。请注意,实际更改不会保留。该通知仅包含特定数据实体已更改的信息。此外,由于变更表的版本控制是累积性的,因此不会保留有关单个项目的变更通知,并由较新的通知覆盖。这意味着,如果实体更改两次,更改跟踪将仅知道最新更改。

为了捕获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消息处理器(以上链接中的示例)通过C#将队列连接到该队列。

与变更跟踪或CDC不同,队列触发不依赖于轮询,从而提供了实时事件。

CLR

这是我见过的一种技术,但我不推荐使用。任何依赖CLR进行外部通信的解决方案充其量都是hack。CLR旨在通过利用C#简化编写复杂数据处理代码的过程。它并非旨在连接外部依赖项(例如消息传递库)。此外,CLR绑定的操作可能会以无法预测的方式在群集环境中中断。

这就是说,设置非常简单,因为您要做的就是向CLR注册消息传递程序集,然后可以使用触发器或SQL作业取消调用。

综上所述...

微软一直坚决拒绝解决这一问题,这一直令我感到惊讶。从数据库到代码的事件应该是数据库产品的内置功能。考虑到Oracle Advanced Queuing与ODP.net MessageAvailable事件相结合已为10年前的C#提供了可靠的数据库事件,这对MS来说是可悲的。

这样做的结果是,这个问题列出的解决方案都不是很好。它们都具有技术缺陷,并且安装成本很高。Microsoft,如果您正在听,请解决这种抱歉的情况。


17

通常,您会使用 Service Broker

那就是触发器->队列->应用程序

看到其他答案后进行编辑:

仅供参考:“查询通知”基于服务代理

编辑2:

更多连结


我已使用SqlSependency触发数据库更改以向客户端显示推送通知,但最近我们移至SQL Azure,它不支持,SqlSependency因此,有没有比更好的方法来在SQL Azure数据更改或插入新数据时获取通知?
shaijut

8

使用SqlTableDependency。记录更改时,这是ac#组件引发事件。您可以在以下位置找到其他详细信息: 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 2015年

当然,它是兼容的
dyatchenko '16

6

SqlDependency不会监视数据库,而是会监视您指定的SqlCommand,因此,如果您试图说在1个项目中向数据库中插入值并在另一个项目中捕获该事件,则该事件将无法正常工作,因为该事件来自SqlCommand。 1º项目不是数据库,因为创建SqlDependency时会将其链接到SqlCommand,并且仅当使用该项目中的命令时,它才会创建Change事件。


4
这实际上是不正确的。即使您在Management Studio中插入值,SqlDependency也可以工作。但是,此类有很多问题,例如内存泄漏。请参阅下面的详细信息。@KayLee
dyatchenko '16

@dyatchenko,感谢您的意见。我正在使用SqlTableDependency,它在本文的一个答案中提到。我现在很忙,但是稍后再看一下内存问题……
凯莉·李


2

看起来就像是糟糕的架构。另外,您还没有指定需要通知的应用程序类型(Web应用程序/控制台应用程序/ winforms /服务等)

但是,要回答您的问题,有多种解决方法。您可以使用:

1)时间戳记(如果您只是想确保第二个应用程序的下一组更新不会与第一个应用程序的更新冲突)

2)sql依赖项对象-有关更多信息,请参见http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldependency.aspx

3)定制的推送通知服务,多个客户端(Web / WinForm /服务)可以订阅并获得更改通知

简而言之,您需要根据通知要求的复杂程度以及使用它们的目的来使用最简单,最容易,最便宜(就工作而言)的解决方案。如果仅需要简单的数据并发,则不要尝试构建过于复杂的通知系统(在这种情况下,请使用基于时间戳的简单解决方案)


19
出于好奇,您能否澄清一下这里的“不良体系结构”?
2012年

1

监视表的另一种非常简单的方法是表版本控制。经验证,该系统可在DNS同步等结构中运行。为了使其正常工作,您需要创建一个包含表名称和表版本的表decimalbigint.在需要监视的每个表中,在插入,更新和删除时创建触发器,这些触发器将在执行时增加版本控制表中的相应表版本。如果您希望任何被监视的表经常更改,则需要准备重新使用版本。最后,在您的应用程序中,每次查询被监视的表时,您还要查询其版本并将其存储。当您从您的应用程序更改受监视的表时,您首先要查询其当前版本并仅在版本未更改的情况下处理更改。您可以将proc存储在sql server上为您工作。这是极其简单但经过验证的可靠解决方案。


我认为这将在包含给定表版本的行周围创建一个锁定瓶颈。
丹德夫

0

这不完全是一个通知,但您在标题中说了“监视”,它可以适应这种情况。

使用“ SQL Server时间戳”列可以使您轻松查看查询之间的任何更改(仍然存在)。

在我看来,SQL Server时间戳记列类型的命名不正确,因为它根本与时间无关,它是数据库范围的值,在任何插入或更新时都会自动递增。您可以在后面的表中选择Max(timestamp),也可以从刚插入的行中返回时间戳,然后选择timestamp> storedTimestamp,这将为您提供在这些时间之间已更新或插入的所有结果。

由于它也是数据库范围的值,因此您也可以使用存储的时间戳来检查自上次检查/更新存储的时间戳以来是否有任何表写入了数据。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.