SqlDependency OnChange 事件未针对 SignalR 触发

SqlDependency OnChange event not firing for SignalR

我知道有多个关于 SO 的问题几乎相同,但不幸的是,在遵循了无数指南并阅读了几个答案之后,我无法回答为什么作为 SignalR 的新用户会出现这种情况 / SqlDependencies.

我有一个 ASP.Net WebForms 应用程序,它使用 SignalR 将实时图形推送到页面。代码在初始加载时执行,并触发事件,但之后,当依赖项使用 OnChange 事件检测到更改时,我无法触发事件。

我可以看到队列和 SP 在服务器上创建得很好,但是当数据 added/removed 或在 table 中更新时,我看不到队列收到任何通知.我认为这可能与 OnChange 重新订阅有关,但我不完全确定。

什么可能导致事件在初始加载后未触发或未收到来自 SQL 的通知?

我已经在下面发布了所有代码:

集线器代码

Namespace SignalR.Models
    <HubName("notificationHub")>
    Public Class NotificationHub
        Inherits Hub

        Private notifCount As Integer

        <HubMethodName("sendNotifications")>
        Public Sub SendNotifications()
            Using db As SqlConnection = New SqlConnection(System.Web.Configuration.WebConfigurationManager.ConnectionStrings("aspCreate_fetch2").ConnectionString)
                Dim query As String = " SELECT IIF(COUNT(l.[id]) > 99, 99, COUNT(l.[id]))
                                      FROM pla.[lv_test] as l
                                     WHERE 1=1
                                          AND l.[lv_int_id] = 419"
                Using sp As SqlCommand = New SqlCommand(query, db)
                    Dim dependency As SqlDependency = New SqlDependency(sp)
                    AddHandler dependency.OnChange, New OnChangeEventHandler(AddressOf dependency_OnChange)

                    sp.Notification = Nothing
                    Dim dt As DataTable = New DataTable()

                    db.Open()

                    If db.State = ConnectionState.Closed Then db.Open()
                    Dim reader = sp.ExecuteReader()
                    dt.Load(reader)

                    If dt.Rows.Count > 0 Then
                        notifCount = Int32.Parse(dt.Rows(0)(0).ToString())
                    End If

                    Dim context = GlobalHost.ConnectionManager.GetHubContext(Of NotificationHub)()
                    context.Clients.All.ReceiveNotification(notifCount)
                End Using
            End Using
        End Sub
        Private Sub dependency_OnChange(sender As Object, e As SqlNotificationEventArgs)
            If e.Type = SqlNotificationType.Change Then
                SendNotifications()
            End If
        End Sub
    End Class
End Namespace

全球 ASAX

    Sub Application_Start(sender As Object, e As EventArgs)
        Dim sConn = System.Web.Configuration.WebConfigurationManager.ConnectionStrings("redacted1").ConnectionString

        ' Fires when the application is started
        RouteConfig.RegisterRoutes(RouteTable.Routes)
        BundleConfig.RegisterBundles(BundleTable.Bundles)
        SqlDependency.[Stop](sConn)
        SqlDependency.Start(sConn)
    End Sub

    Private Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
        Dim sConn = System.Web.Configuration.WebConfigurationManager.ConnectionStrings("redacted1").ConnectionString
        SqlDependency.[Stop](sConn)
    End Sub

JavaScript

$(function () {
    var nf = $.connection.notificationHub;

    nf.client.receiveNotification = function (notifCount) {
        console.log("connection started");
        $("#notif-badge").text(notifCount);
    }

    $.connection.hub.start().done(function () {
        nf.server.sendNotifications();
    }).fail(function (e) {
        alert(e);
    });
});

我不是 VB 或 Javascript 专家,但我相信您对 OnChange 的订阅在退出 SendNotifications() 后立即被删除。

在您的代码中,您具有以下依赖项:

SqlDependency -> SqlCommand -> SqlConnection

并且您正在附加到 SqlDependency 对象。但是,因为您的 SqlConnection 是在方法的末尾处理的,所以您的订阅就没有了。

将您的 SqlConnection 声明为私有 属性 并保持连接打开。此外,将事件订阅移动到单独的初始化方法或构造函数中仅执行一次。

编辑 :

这里大致是我的想法(在 C# 中,对不起 ^^ ):

public class DemoSqlSubscriber : Hub
{  
    readonly string connectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings("aspCreate_fetch2").ConnectionString;  
    private SqlDependency dependency;
        
    public void StartListening()  
    {  
        SqlDependency.Start(connectionString);  
        SqlConnection connection = new SqlConnection(connectionString);  
        connection.Open();  

        SqlCommand command=new SqlCommand();  
        command.CommandText= "SELECT [notification_count] FROM pla.[notif_count]";  
        command.Connection = connection;  
        command.CommandType = CommandType.Text;  

        dependency = new SqlDependency(command);  
        dependency.OnChange += new OnChangeEventHandler(OnCountChanged);  
    }  

    private void OnCountChanged(object s,SqlNotificationEventArgs e)  
    {  
        if(e.Type == SqlNotificationType.Change)
        {
            // Publish 
            IHubContext<NotificationHub> context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
            context.Clients.All.ReceiveNotification(notifCount);
        }
    }  

    public void StopListening()  
    {  
        SqlDependency.Stop(connectionString);  
    }  
} 

您能否尝试在 VB 中相应地构建您的 Hub。 NET 让我们知道?

通知查询有很多限制,详见here。限制之一是:

The statement must not use any of the following aggregate functions: AVG, COUNT(*), MAX, MIN, STDEV, STDEVP, VAR, or VARP.

如果查询对于通知订阅无效,OnChange 处理程序将立即触发 SqlNotificationType.Invalid

下面是 SqlNotificationEventArgs 属性 我得到的值 运行 一个类似于你的查询(即使用 COUNT 聚合函数) OnChange被调用:

Info=Invalid, Source=Statement, Type=Subscribe

但是,您的处理程序代码会默默地忽略无效订阅,因为它只检查 SqlNotificationType.Change