SqlDependency立即触发

时间:2015-04-01 13:44:07

标签: c# sql sql-server

我想使用SqlDependency在使用数据库的其他应用程序更改某些数据时收到通知。

public class DatabaseChangesNotification : IDisposable
{
    private static string chaineDeConnexion = ConfigurationManager.ConnectionStrings["TransfertContext"].ConnectionString;

    private static readonly Lazy<DatabaseChangesNotification> _instance = new Lazy<DatabaseChangesNotification>(() => new DatabaseChangesNotification());

    private DatabaseChangesNotification()
    {
        System.Diagnostics.Trace.WriteLine("--- SqlDependency START ---");
        SqlDependency.Start(chaineDeConnexion);
    }

    public void Dispose()
    {
        System.Diagnostics.Trace.WriteLine("--- SqlDependency STOP ---");
        SqlDependency.Stop(chaineDeConnexion);
    }

    public static DatabaseChangesNotification Instance
    {
        get 
        {
            return _instance.Value; 
        }
    }

    public void AbonnerNotification(string requete, OnChangeEventHandler eventhandler)
    {
        using (SqlConnection connection = new SqlConnection(chaineDeConnexion))
        {
            using (SqlCommand command = new SqlCommand(requete, connection) { Notification = null }) // clear existing notifications
            {
                connection.Open();

                var sqlDependency = new SqlDependency(command);

                OnChangeEventHandler delegateAutoRemove = null;
                delegateAutoRemove = (sender, e) => {
                    var dependency = sender as SqlDependency;
                    dependency.OnChange -= delegateAutoRemove;
                    eventhandler(sender, e); 
                };

                sqlDependency.OnChange += delegateAutoRemove;

                command.ExecuteNonQuery();
            }
        }
    }
}

因此,只需一行我就可以注册一个事件处理程序:

DatabaseChangesNotification.Instance.AbonnerNotification(@"SELECT IdUtilisateur, Code, Nom, Prenom, NomComplet, Login, Synchroniser FROM dbo.Utilisateur", OnChanges);  

    public void OnChanges(object sender, SqlNotificationEventArgs e){
        System.Diagnostics.Trace.WriteLine("------------------------------ UPDATTEEEE -------------------------");
        System.Diagnostics.Trace.WriteLine("Info:   " + e.Info.ToString());
        System.Diagnostics.Trace.WriteLine("Source: " + e.Source.ToString());
        System.Diagnostics.Trace.WriteLine("Type:   " + e.Type.ToString());

        GlobalHost.ConnectionManager.GetHubContext<TransfertClientHub>().Clients.All.hello("users modified !");
        //AbonnementChanges();
    }

但我的问题是通知立即被解雇了:

--- ABONNEMENT ---
------------------------------ UPDATTEEEE -------------------------
Info:   Query
Source: Statement
Type:   Subscribe

这就是我在事件处理程序AbonnementChanges中评论OnChanges的原因(或者它将无限循环)。

我不知道问题出在哪里,因为我重置了通知({ Notification = null }),我的请求符合要求(https://msdn.microsoft.com/en-us/library/ms181122.aspx)。

修改:我想补充说select * from sys.dm_qn_subscriptions不返回任何内容。

编辑:看起来它来自数据库配置,而不是来自我的实现,因为我尝试了另一种导致相同行为的实现:http://www.codeproject.com/Articles/144344/Query-Notification-using-SqlDependency-and-SqlCach

编辑:我没有看到它来自哪里,因为我使用SA是sysadmin并拥有所有权利,不是吗?

编辑:我尝试按照本教程定义与数据库的另一个连接:http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen

所以我创造了两个角色:

EXEC sp_addrole 'sql_dependency_subscriber' 
EXEC sp_addrole 'sql_dependency_starter' 

-- Permissions needed for [sql_dependency_starter]
GRANT CREATE PROCEDURE to [sql_dependency_starter] 
GRANT CREATE QUEUE to [sql_dependency_starter]
GRANT CREATE SERVICE to [sql_dependency_starter]
GRANT REFERENCES on 
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
  to [sql_dependency_starter] 
GRANT VIEW DEFINITION TO [sql_dependency_starter] 

-- Permissions needed for [sql_dependency_subscriber] 
GRANT SELECT to [sql_dependency_subscriber] 
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [sql_dependency_subscriber] 
GRANT RECEIVE ON QueryNotificationErrorsQueue TO [sql_dependency_subscriber] 
GRANT REFERENCES on 
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
  to [sql_dependency_subscriber] 

然后我将用户(production)添加到此角色:

- 确保我的用户是正确角色的成员。

EXEC sp_addrolemember 'sql_dependency_starter', 'production'
EXEC sp_addrolemember 'sql_dependency_subscriber', 'production'

但是通过这种联系,我有与以前相同的行为。通知是imediatly解雇的:

------------------------------ UPDATTEEEE -------------------------
Info:   Query
Source: Statement
Type:   Subscribe

修改:我尝试了更简单的请求,例如:SELECT Nom, Prenom FROM dbo.Utilisateur。 以下是应该检查的表的详细信息:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[Utilisateur](
    [IdUtilisateur] [uniqueidentifier] ROWGUIDCOL  NOT NULL CONSTRAINT [DF_Utilisateur_IdUtilisateur]  DEFAULT (newid()),
    [Code] [varchar](10) NOT NULL,
    [Nom] [varchar](100) NOT NULL,
    [Prenom] [varchar](100) NULL,
    [NomComplet]  AS (([Prenom]+' ')+[Nom]),
    [Login] [varchar](50) NULL,
    [Synchroniser] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Synchroniser]  DEFAULT ((1)),
    [DATE_CREATION] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2AA1E7C7]  DEFAULT (getdate()),
    [DATE_DERNIERE_MODIF] [datetime] NOT NULL CONSTRAINT [DF__Utilisate__DATE___2B960C00]  DEFAULT (getdate()),
    [Desactive] [bit] NOT NULL CONSTRAINT [DF_Utilisateur_Desactive]  DEFAULT ((0)),
 CONSTRAINT [PK_Utilisateur] PRIMARY KEY CLUSTERED 
(
    [IdUtilisateur] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

正如我们所看到的,有些列无法请求。这就是我不使用它的原因。

现在让我们查看SELECT Nom, Prenom FROM dbo.Utilisateur

  • SELECT语句中的预计列必须是显式的 声明,表名必须用两部分名称限定。注意 这意味着语句中引用的所有表必须是 在同一个数据库中。的确定
  • 该语句可能不使用星号()或table_name。语法 指定列。的确定
  • 该语句不能使用未命名的列或重复的列名。的确定
  • 该语句必须引用基表。的确定
  • SELECT语句中的投影列可能不包含 聚合表达式,除非该语句使用GROUP BY 表达。提供GROUP BY表达式时,选择列表 可以包含聚合函数COUNT_BIG()或SUM()。然而, 不能为可空列指定SUM()。的确定
  • 该声明可能未指定HAVING,CUBE或ROLLUP。预计 SELECT语句中用作简单表达式的列 一定不能出现多次。的确定
  • 声明不得包含PIVOT或UNPIVOT运营商。的确定
  • 声明不得包含INTERSECT或EXCEPT运算符。的确定
  • 声明不得引用视图。的确定
  • 声明不得包含以下任何内容:DISTINCT, COMPUTE或COMPUTE BY,或INTO。的确定
  • 该语句不得引用服务器全局变量 (@@变量名)。的确定
  • 该语句不得引用派生表,临时表或 表变量。的确定
  • 该语句不得引用其他数据库中的表或视图 或服务器。的确定
  • 语句不得包含子查询,外连接或 自联接。的确定
  • 语句不能引用大对象类型:text,ntext, 和图像。的确定
  • 声明不得使用CONTAINS或FREETEXT全文 谓词。的确定
  • 语句不能使用行集函数,包括OPENROWSET和 OPENQUERY。的确定
  • 声明不得使用以下任何聚合函数: AVG,COUNT(*),MAX,MIN,STDEV,STDEVP,VAR或VARP。的确定
  • 声明不得使用任何非确定性函数,包括 排名和窗口函数。的确定
  • 该语句不得包含用户定义的聚合。的确定
  • 该语句不得引用系统表或视图,包括 目录视图和动态管理视图。的确定
  • 声明不得包含FOR BROWSE信息。的确定
  • 该语句不得引用队列。的确定
  • 该语句不得包含不能的条件语句 更改并且无法返回结果(例如,WHERE 1 = 0)。的确定

但那仍然行不通...... =(

最终编辑 - 解决方案:正如Jon Tirjan所说,这是由我的计算列NomComplet引起的,该列对Service Broker无效(即使我不要求通知了这一栏的变化,这对我来说很奇怪。)

2 个答案:

答案 0 :(得分:5)

Service Broker不适用于具有计算列的表。您需要从表中删除NomComplet,或将其更改为以另一种方式填充的实际列(触发器,存储过程等)

由于设置队列时发生错误,因此会立即触发通知。

答案 1 :(得分:0)

感谢George Stocker删除我之前的回答,但我认真issue SqlDependency并坚持:

  

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

对于我的项目,我使用了开源实现 - SqlDependencyEx。它使用数据库触发器和本机Service Broker通知来接收有关表更改的事件。这是一个用法示例:

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

使用SqlDependecyEx,您可以分别监控INSERTDELETEUPDATE,并在事件args对象中接收实际更改的数据(xml)。希望这有帮助。

相关问题