Web 表单中的 SQL 依赖关系 - 检测更改

问题描述 投票:0回答:1

我之前没有在实践中使用过 SQL 依赖关系,但据我了解,

DependencyOnChange
当我运行下面的 SQL 更新语句时应该触发事件。

澄清一些细节:

  • is_broker_enabled
    = 1
  • SQL 用户是
    db_owner
  • SqlDependency_Register
    在页面按预期加载时运行。
  • 更新
    DependencyOnChange
    时,不会命中
    MyCol
    上的断点。
  • 我知道需要 SignalR 来向客户端发送数据,但我仍然期望在调试时命中 DependencyOnChange 内的代码。

此时任何建议或指导将不胜感激,因为我已经用尽了我能找到的所有研究选项。

SQL

UPDATE [dbo].[MyTable] SET [MyCol] ='test' WHERE [id] = 1`

VB.NET

Private Sub Page_PreRender(sender As Object, e As EventArgs) Handles Me.PreRender
  If Not IsPostBack Then
    SqlDependency_Register()
  End If
End Sub

Protected Sub Page_Unload(sender As Object, e As EventArgs) Handles Me.Unload
  SqlDependency.Stop(ConfigurationManager.ConnectionStrings("CON").ConnectionString)
End Sub

Public Sub SqlDependency_Register()
  Using con As New SqlConnection(ConfigurationManager.ConnectionStrings("CON").ConnectionString)
    Using cmd As New SqlCommand("SELECT [MyCol] FROM [dbo].[MyTable]", con)
      Dim dependency As New SqlDependency(cmd)
      AddHandler dependency.OnChange, AddressOf DependencyOnChange
      SqlDependency.Start(ConfigurationManager.ConnectionStrings("CON").ConnectionString)
      con.Open()
      cmd.ExecuteReader()
    End Using
  End Using
End Sub

Private Sub DependencyOnChange(sender As Object, e As SqlNotificationEventArgs)

  If e.Info = SqlNotificationInfo.Update Then
    Console.WriteLine(e.Info)
  End If

  SqlDependency_Register()
End Sub

目标

当 sql 中的数据值发生变化时,在页面上显示数据的实时更新(例如通过另一个用户)。我了解通过 Signalr 从服务器向客户端发送数据,但不了解如何让 .Net 应用程序检测 SQL 中的更改。

也许有比 SQLDependency 更好的方法?

.net sql-server vb.net service-broker sqldependency
1个回答
1
投票

好吧,请记住,给定页面的后台代码超出了范围,并在页面发送到客户端后立即被删除、处置和从内存中删除。

如前所述,您可以考虑使用 SignalR,但您不能坚持认为给定网页背后的代码存在。它仅在回发期间存在。一旦页面发送到客户端,代码、变量和所有内容都会从内存中删除。 Web 服务器与桌面软件不同,每个用户都有一组持久的代码和变量。

在网络领域,这个术语是“无状态”。这意味着您可以将网页(及其代码)视为调用子例程。当您退出该子例程时,该子例程中的所有代码和变量都会消失,并超出范围。您的网页代码的工作方式相同 - 它仅在短回发期间存在。一旦后台代码完成运行并将页面发送回客户端,那么所有后台代码现在都会超出范围,从内存中删除,并且不会持久存在。

所以,最简单的解决方案是确保数据库有行版本值。当该行数据更新时,该值也会更改。

因此,一个简单且可行的方法是让一些客户端代码调用 Web 方法,并返回行版本值的 sum()。如果发生任何更新、删除或插入,则该值将会更改。

但是,如前所述,您不能让 SQL Server 事件调用您的页面代码,因为正如我所指出的,页面代码(页面类)不存在于内存中,并且在页面发送到后立即从内存中删除。客户端。

因此,我想您可以启动一个单独的进程,该单独的进程可以跟踪页面是否已更改,然后当然使用 SignalR,然后就可以工作了。但是您不能使用给定网页的后台代码来监视此类数据库事件,因为后台代码不在内存中也不再运行。

除非您想采用一些复杂的 SignalR 代码并保持进程在服务器端运行?

然后我建议在该给定页面上使用一个简单的 Web 方法,并让客户端检查更改,例如每 1 或 2 秒检查一次。

所以,这是一个有效的示例:

标记:

        <asp:GridView ID="GVHotels" runat="server" AutoGenerateColumns="False"
            DataKeyNames="ID" CssClass="table table-hover"
            Width="50%">
            <Columns>
                <asp:BoundField DataField="FirstName" HeaderText="FirstName" />
                <asp:BoundField DataField="LastName" HeaderText="LastName" />
                <asp:BoundField DataField="City" HeaderText="City" />
                <asp:BoundField DataField="HotelName" HeaderText="Hotel" />
                <asp:BoundField DataField="Description" HeaderText="Descriptioin" />
            </Columns>
        </asp:GridView>

        <asp:HiddenField ID="MyChanged" runat="server" Value="0" ClientIDMode="Static" />
        <asp:Button ID="cmdReload" runat="server" Text="Reload grid"
            OnClick="cmdReload_Click" />

    </div>

    <script>

        var MyTimer
        $(window).on("load", function () {
            MyTimer = setInterval(checkchanged, 2000)
        });

        function checkchanged() {
            $.ajax({
                url: "Hotels.aspx/GetChanged",
                data: {},
                dataType: "json",
                type: "POST",
                contentType: "application/json; charset=utf-8",
                success: function (data) {
                    if (data.d != $('#MyChanged').val()) {
                        $('#cmdReload').click()
                    }
                }
            });
        }

    </script>

背后的代码:

Imports System.Web.Services

因此:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not IsPostBack Then
        LoadData()      ' first time grid load
    End If

End Sub

Sub LoadData()

    Dim rstData As DataTable
    Dim strSQL As String =
        "SELECT *, cast(ts as bigint) as checksum FROM tblHotelsA 
        ORDER BY HotelName"

    rstData = MyRst(strSQL)
    GVHotels.DataSource = rstData
    GVHotels.DataBind()

    Dim checksum As ULong = 0
    For Each dr As DataRow In rstData.Rows
        checksum += dr("checksum")
    Next

    MyChanged.Value = checksum

End Sub


Protected Sub cmdReload_Click(sender As Object, e As EventArgs)

    LoadData()

End Sub

<WebMethod>
Public Shared Function GetChanged() As String

    Dim rstData As DataTable
    Dim strSQL As String =
        "SELECT sum(cast(ts as bigint)) as checksum FROM tblHotelsA"

    rstData = MyRst(strSQL)
    Dim Result As ULong = rstData.Rows(0)(0)

    Return Result.ToString

End Function

所以,结果是这样的:

因此,当数据发生更改(更新、删除或编辑)时,GridView 将重新加载。我使用 jQuery .click() 事件单击页面上的按钮进行刷新,当然该按钮可以(应该)隐藏,如下所示:

        <asp:Button ID="cmdReload" runat="server" Text="Reload grid"
            OnClick="cmdReload_Click"
            style="display:none"
            />

所以,我认为上述内容比采用 SignalR 并推送到浏览器要少得多。即使您确实采用 SignalR,您仍然无法让给定页面的后台代码从 SQL Server 订阅某些“事件”,因为如上所述,页面类(后台代码)不存在并且立即脱离上下文页面已发送至客户端。

您注意到我向网页添加了一个 Web 方法,但它是一个“静态”方法,因此必须标记为“共享”(在 VB.NET 中),或者在 C# 中标记为静态,因为调用该 Web 方法时,不会创建页面类(隐藏代码)。

© www.soinside.com 2019 - 2024. All rights reserved.