【问题标题】:SqlDependency OnChange Event Fires many times for every single event on DatabaseSqlDependency OnChange 事件为数据库上的每个事件触发多次
【发布时间】:2023-04-01 04:20:02
【问题描述】:

我正在使用 SqlDependency 和 signalR 开发通知系统,我无法处理的问题是当我根据与会者状态将 DB 中的属性值“IsOnline”更改为 True 或 False 时,OnChange 事件会触发多次,第一次新用户登录时,我收到两个通知,然后第二次收到更多通知,然后更多。每次有新用户登录或退出时,通知的数量都会增加。我确定 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();
        }
    }

【问题讨论】:

    标签: c# asp.net webforms signalr sqldependency


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      我将计数器变量初始化为全局变量。在“扫描”任何更改之前的临时数据状态后,我还将 counter 的值设置为 0。

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

          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
                  }
           }
      

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

      【讨论】:

        【解决方案3】:

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

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

        无法从您的帖子中判断这是否会导致您的情况出现问题,具体而言,不清楚您如何处理更新,从而导致另外 4 条通知。收到 4 条通知意味着您发布了 4 条要通知的查询,因此很可能您的代码也存在问题并且您超额订阅了。

        阅读Using SQL Trace to Troubleshoot Query Notifications 并尝试解决正在发生的事情,通知在哪里创建和失效。

        【讨论】:

          【解决方案4】:

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

          该行被多次执行。该事件应仅订阅一次。 oDependency.OnChange += new OnChangeEventHandler(DBUpdateNotificationReeived);

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-01-16
            • 1970-01-01
            • 1970-01-01
            • 2011-11-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多