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

时间:2011-03-13 09:05:48

标签: c# sql-server monitoring

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

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

11 个答案:

答案 0 :(得分:48)

您可以使用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 (基于消息的通信平台)从数据库引擎接收消息。

答案 1 :(得分:25)

为了完整性,我认为还有一些其他解决方案比依赖于SqlDependency(和SqlTableDependency)类的解决方案更正统,更完善。 SqlDependency是为Web服务器缓存刷新而设计的,因此实际上并没有在事件生产者需要的负载下提供这种弹性。

这里还没有提到其他四个选项:

  • 更改跟踪
  • CDC
  • 触发队列
  • CLR

更改跟踪

来源:https://docs.microsoft.com/en-us/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进行外部通信的任何解决方案都是充其量的黑客攻击。 CLR旨在通过利用C#使编写复杂的数据处理代码变得更容易。它不是设计用于连接外部依赖项,如消息库。此外,CLR绑定操作可能以不可预测的方式在集群环境中中断。

这就是说,设置起来相当简单,因为您需要做的就是使用CLR注册消息传递程序集,然后使用触发器或SQL作业调用它。

总结......

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

这样做的结果是,这个问题中列出的解决方案都不是很好。它们都具有技术缺陷并且具有显着的设置成本。微软,如果你正在倾听,请理清这种令人遗憾的事态。

答案 2 :(得分:17)

通常,您使用Service Broker

这是触发器 - &gt;队列 - &gt; (多个)应用

在看到其他答案后编辑:

仅供参考:“查询通知”建立在服务代理

之上

EDIT2:

更多链接

答案 3 :(得分:7)

使用SqlTableDependency。当记录发生变化时,它是一个c#组件引发事件。 您可以在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);
    }
}

答案 4 :(得分:6)

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

答案 5 :(得分:5)

小心使用SqlDependency类 - 它有problems内存泄漏。

只需使用跨平台的.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 :(得分:4)

从SQL Server 2005开始,您可以选择使用Query Notifications,ADO.NET可以利用它来查看 http://msdn.microsoft.com/en-us/library/t9x04ed2.aspx

答案 7 :(得分:2)

一直看起来像糟糕的建筑。你还没有指定你需要通知的应用程序类型(web app / console app / winforms / service等)

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

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

2)sql依赖项对象 - 有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldependency.aspx

3)自定义推送通知服务,多个客户(web / winform / service)可以订阅并获得有关更改的通知

简而言之,您需要使用最简单,最简单和最便宜(在努力方面)的解决方案,具体取决于您的通知要求的复杂程度以及您需要使用它们的目的。如果简单数据并发是您唯一的要求(在这种情况下,请使用简单的基于时间戳的解决方案),请不要尝试构建过于复杂的通知系统

答案 8 :(得分:1)

监视表的另一种非常简单的方法是表版本控制。事实证明,该系统可用于DNS同步等结构。要使其工作,您需要创建一个包含表名和表版本的表decimalbigint.在您需要监视的每个表中,在插入,更新和删除时创建触发器,这将在版本控制中增加适当的表版本表格执行时。如果您希望经常更改任何受监控的表,则需要配置版本重用。最后,在您的应用程序中,每次查询受监控的表时,还会查询其版本并存储它。当您从应用程序更改受监控的表时,首先查询其当前版本并仅在版本未更改时处理更改。您可以在sql server上存储proc,这对您有用。 这是非常简单但经过验证的可靠解决方案它具有特定的功能用途(以确保数据一致性)并且资源很少(您不会引发您不会注意的代理事件),但需要应用程序主动检查更改而不是被动地等待事件发生。

答案 9 :(得分:0)

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

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

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

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

答案 10 :(得分:-2)

使用AMQP的其他语言的golang realization中的

ServiceBrokerListener enter image description here