【发布时间】:2015-04-22 12:32:01
【问题描述】:
我想使用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
这就是我在事件处理程序OnChanges 中评论AbonnementChanges 的原因(否则它将无限循环)。
我不知道问题出在哪里,因为我重置了通知 ({ 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,并且拥有所有权限,不是吗?
编辑:我尝试按照本教程定义另一个到数据库的连接:http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen
所以我创建了 2 个角色:
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'
但是通过这种连接,我的行为与以前相同。立即触发通知:
------------------------------ 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 和 公开查询。 好的
- 该语句不得使用以下任何聚合函数: AVG、COUNT(*)、MAX、MIN、STDEV、STDEVP、VAR 或 VARP。 好的
- 该语句不得使用任何非确定性函数,包括 排序和窗口函数。 好的
- 语句不得包含用户定义的聚合。 好的
- 语句不得引用系统表或视图,包括 目录视图和动态管理视图。 好的
- 语句不得包含 FOR BROWSE 信息。 好的
- 语句不能引用队列。 好的
- 语句不能包含不能 更改并且无法返回结果(例如,WHERE 1=0)。 好的
但这仍然行不通... =(
最终编辑 - 解决方案: 正如 Jon Tirjan 所说,这是由我的计算列 NomComplet 引起的,这对 Service Broker 无效(即使我不要求在这个专栏的变化,这对我来说很奇怪)。
【问题讨论】:
-
我想补充一点,
select * from sys.dm_qn_subscriptions什么都不返回 是的,SqlDependency在收到通知后会取消订阅。您的事件已触发,不再订阅。 -
没错。我也是这么理解的。
-
下次请不要在 DBA.SE 上cross post this。
-
好的,我删除了另一个帖子。我不知道这是禁止的。
-
您是否使用存储过程进行数据操作?根据codeproject.com/Articles/12335/…,在您的存储过程中使用
SET NOCOUNT ON将使其在查询通知中的使用无效。
标签: c# sql sql-server