SqlDependency OnChange事件为Database上的每个事件触发多次

时间:2015-07-27 09:03:07

标签: c# asp.net webforms signalr sqldependency

我正在使用SqlDependency和signalR开发通知系统,我无法处理的问题是当我更改属性值时#34; IsOnline"根据与会者状态,在DB为True或False时,OnChange事件会多次触发,第一次新用户登录时我得到两个通知,然后第二次我得到更多像4然后更多。每次新的登录或退出时,通知的数量都会增加。我确定SqlDependency中的问题不在SignalR中,我将与您分享我的部分代码。

提前致谢。

  [System.Web.Services.WebMethod]

    public static IEnumerable<AttendeeList> GetAllUsers()
    {
        var AttendeeList = new List<AttendeeList>();

        try
        {
            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                string str = "";
                str += "SELECT [AttendeeID], ";
                str += "       [IsAllowToUploadDocuments],";
                str += "       [IsOnline], ";
                str += "       [AttendeeTypeName],";
                str += "       [UserName] ";
                str += "       FROM [dbo].[Meeting_Attendees]   ";
                str += "       INNER JOIN [dbo].[aspnet_Users]  ON [aspnet_Users].[UserId] = [Meeting_Attendees].[AttendeeID] ";
                str += "       INNER JOIN   [dbo].[AttendeeType] ON [dbo].[AttendeeType].[AttendeeTypeID] = [dbo].[Meeting_Attendees].[AttendeeTypeID] ";
                str += "       WHERE [MeetingID]=@MeetingID ORDER BY [IsOnline] DESC";

                using (var command = new SqlCommand(@str, connection))
                {
                    SqlParameter prm = new SqlParameter("@MeetingID", SqlDbType.Int);
                    prm.Direction = ParameterDirection.Input;
                    prm.DbType = DbType.Int32;
                    prm.Value = Convert.ToInt32(Properties.Settings.Default.MeetingID);
                    command.Parameters.Add(prm);
                    command.Notification = null;

                    var dependency = new SqlDependency(command);
                    dependency.OnChange += new OnChangeEventHandler(dependencyUsers_OnChange);

                    if (connection.State == ConnectionState.Closed)
                        connection.Open();

                    var reader = command.ExecuteReader();

                    while (reader.Read())
                    {
                        AttendeeList.Add(item: new AttendeeList { UserName = (string)reader["UserName"], UserType = (string)reader["AttendeeTypeName"], IsOnline = (bool)reader["IsOnline"], IsAllowToUploadDocuments = (bool)reader["IsAllowToUploadDocuments"], IsCurrentUser = true ? (Guid)reader["AttendeeID"] == new Guid(Properties.Settings.Default.UserID.ToString()) : false });
                    }
                }
            }
        }
        catch { }
        return AttendeeList;
    }

    private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
    {
        if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update)
        {
            //Call SignalR  
            MessagesHub.UpdateUsers();
        }
    }

4 个答案:

答案 0 :(得分:2)

要确保事件处理程序已注册一次,请在“ + =”之前执行“-=”:

oDependency.OnChange -= new OnChangeEventHandler(DBUpdateNotificationReeived);
oDependency.OnChange += new OnChangeEventHandler(DBUpdateNotificationReeived);

检查您的SQL表是否没有“更新”正在更新的记录的触发器(除了sqltabledependency之外)。

答案 1 :(得分:0)

如果监控结果集可能已更改,则会触发查询通知,请参阅Understanding When Query Notifications Occur。作为一般规则,您可能会获得比实际数据更改更多的通知:

  

请注意,SQL Server可能会生成查询通知,以响应不更改数据的事件,或响应不会实际影响查询结果的更改。例如,当UPDATE语句更改查询返回的行之一时,即使对行的更新未更改查询结果中的列,通知也可能会触发。查询通知旨在支持改进的总体目标缓存数据的应用程序的性能。当服务器负载很重时,SQL Server可能会为订阅生成查询通知消息,而不是执行确定查询结果是否已更改的工作。

在您的帖子中是否会导致问题无法分析,特别是不清楚您如何处理更新,以便再生成4个通知。获取4个通知意味着您发布了4个查询以进行通知,因此您的代码中也可能存在问题并且您超额订阅。

阅读Using SQL Trace to Troubleshoot Query Notifications并尝试对正在进行的操作进行问题排查,通知的创建位置和失效位置。

答案 2 :(得分:0)

使用SignalR和SQL Dependency时遇到同样的问题

这条线不止一次被执行。该活动应仅订阅一次。  oDependency.OnChange + = new OnChangeEventHandler(DBUpdateNotificationReeived);

答案 3 :(得分:0)

我的项目中对OnChange事件的多次调用也遇到了同样的问题,但我使用help counter 变量修复了它。在这个例子中,在我的例子中,示例中的dependencyUsers_OnChange函数触发两次

我将计数器变量初始化为全局变量。在&#34;扫描&#34;在任何更改之前您的临时数据的状态,我还将计数器的值设置为0.

按照您的示例,在此步骤之后,在dependencyUsers_OnChange语句if处进行了修改:

    private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
    {
            if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update && counter == 0)
            {
                //Call SignalR  
                MessagesHub.UpdateUsers();
                counter++; //The update is done once
            }
            else 
            {
                counter = 0; //if the update is needed in the same iteration, please don't update and set the counter to 0
            }
     }

在您的情况下,解决方案将类似于:

int counter = 0; //initialization of help counter
[System.Web.Services.WebMethod]

    public static IEnumerable<AttendeeList> GetAllUsers()
    {
        var AttendeeList = new List<AttendeeList>();

        try
        {
            using (var connection = new SqlConnection(_connString))
            {
                connection.Open();
                string str = "";
                str += "SELECT [AttendeeID], ";
                str += "       [IsAllowToUploadDocuments],";
                str += "       [IsOnline], ";
                str += "       [AttendeeTypeName],";
                str += "       [UserName] ";
                str += "       FROM [dbo].[Meeting_Attendees]   ";
                str += "       INNER JOIN [dbo].[aspnet_Users]  ON [aspnet_Users].[UserId] = [Meeting_Attendees].[AttendeeID] ";
                str += "       INNER JOIN   [dbo].[AttendeeType] ON [dbo].[AttendeeType].[AttendeeTypeID] = [dbo].[Meeting_Attendees].[AttendeeTypeID] ";
                str += "       WHERE [MeetingID]=@MeetingID ORDER BY [IsOnline] DESC";

                using (var command = new SqlCommand(@str, connection))
                {
                    SqlParameter prm = new SqlParameter("@MeetingID", SqlDbType.Int);
                    prm.Direction = ParameterDirection.Input;
                    prm.DbType = DbType.Int32;
                    prm.Value = Convert.ToInt32(Properties.Settings.Default.MeetingID);
                    command.Parameters.Add(prm);
                    command.Notification = null;

                    var dependency = new SqlDependency(command);
                    counter = 0; //Whenewer the web method is called, set te counter to 0
                    dependency.OnChange += new OnChangeEventHandler(dependencyUsers_OnChange);

                    if (connection.State == ConnectionState.Closed)
                        connection.Open();

                    var reader = command.ExecuteReader();

                    while (reader.Read())
                    {
                        AttendeeList.Add(item: new AttendeeList { UserName = (string)reader["UserName"], UserType = (string)reader["AttendeeTypeName"], IsOnline = (bool)reader["IsOnline"], IsAllowToUploadDocuments = (bool)reader["IsAllowToUploadDocuments"], IsCurrentUser = true ? (Guid)reader["AttendeeID"] == new Guid(Properties.Settings.Default.UserID.ToString()) : false });
                    }
                }
            }
        }
        catch { }
        return AttendeeList;
    }

    private static void dependencyUsers_OnChange(object sender, SqlNotificationEventArgs e)
    {
            if (e.Type == SqlNotificationType.Change && e.Info == SqlNotificationInfo.Update && counter == 0)
            {
                //Call SignalR  
                MessagesHub.UpdateUsers();
                counter++; //The update is done once
            }
            else 
            {
                counter = 0; //if the update is needed in the same iteration, please don't update and set the counter to 0
            }
     }

我希望这个想法能对某人有所帮助,我用这个解决方案解决了我项目中的问题。