1

我正在编写一个小型 postIt 程序,它应该使用户能够使用数据库向其他计算机发送小笔记。我有一个主表单,我可以在其中编写消息并在线查看用户,一个应该显示消息的工作表表单和一个包含用户和消息表的 sql 数据库。

这个想法是我正在将消息写入 Messagestable 并且我的程序使用SqlDependency来获取新消息并在工作表中显示它们。到目前为止,一切都很好。

问题是当我向表中添加新消息时,SqlDependency 会触发一个事件,并且我的 mainForm 创建一个新工作表,该工作表在调用 sheet.Show() 后冻结。mainForm 继续运行,但我的工作表总是没有响应。

这是我的代码:DBListener:

 Imports System.Data
    Imports System.Data.Sql
    Imports System.Data.SqlClient
    Imports System.Threading

Public Class DBListener

    Private changeCount As Integer = 0

    Private Const tableName As String = "IMSMessages"
    Private Const statusMessage As String = _
       "{0} changes have occurred."
    Private exitRequested As Boolean = False
    Private waitInProgress As Boolean = False
    Private recieverHost As String
    Private imsMain As IMSMain

    Public Sub New(main As IMSMain, recieverHost As String)
        Me.imsMain = main
        Me.recieverHost = recieverHost
        initCommand(recieverHost)
        SqlDependency.Start(connectionString)
        GetMsg(recieverHost)
    End Sub


    Private connectionString As String = "Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;"
    Private sqlConn As SqlConnection = New SqlConnection(connectionString)
    Private commandMesg As SqlCommand = Nothing
    Private commandUsers As SqlCommand = Nothing

    Private Sub initCommand(recieverHost As String)
        commandMesg = New SqlCommand
        commandMesg.CommandText = "SELECT MessageID,SenderHost,RecieverHost,isRead,isRecieved,Stamp from dbo.IMSMessages " &
                                       "WHERE RecieverHost=@RecieverHost" &
                                       " AND isRecieved = 0"
        commandMesg.Parameters.Add("@RecieverHost", SqlDbType.VarChar).Value = recieverHost
        commandMesg.Connection = sqlConn

        commandUsers = New SqlCommand
        commandUsers.CommandText = "Select HostName From dbo.IMSUser"
    End Sub




    Private Sub OnChangeMsg(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
        Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
        RemoveHandler dep.OnChange, AddressOf OnChangeMsg
        GetMsg(recieverHost)
    End Sub
    Private Sub OnChangeUser(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
        Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
        RemoveHandler dep.OnChange, AddressOf OnChangeUser
        GetUsers()
    End Sub

    Public Sub GetMsg(recieverHost As String)
        If Not sqlConn.State = ConnectionState.Open Then
            sqlConn.Open()

        End If

        commandMesg.Notification = Nothing
        Dim dep As SqlDependency = New SqlDependency(commandMesg)
        AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeMsg)
        Dim table As DataTable = New DataTable
        Dim adapter As SqlDataAdapter = New SqlDataAdapter(commandMesg)
        adapter.Fill(table)
        imsMain.recieveNewMessages(table)
    End Sub
    Public Sub GetUsers()
        If Not sqlConn.State = ConnectionState.Open Then
            sqlConn.Open()

        End If

        commandMesg.Notification = Nothing
        Dim dep As SqlDependency = New SqlDependency(commandUsers)
        AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeUser)
        Dim table As DataTable = New DataTable   
        imsMain.updateOnlineUserList()
    End Sub


End Class

IMSMain-窗体:

 Imports System.Data.SqlClient
    Imports System.Threading

    Public Class IMSMain

    Private user As IMSUser
    Private listener As DBListener


    Private sqlConn As SqlConnection

    Public Sub New()
        InitializeComponent()
        sqlConn = New SqlConnection("Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;")



        Me.user = New IMSUser(My.Computer.Name)
        user.register()

        updateOnlineUserList()
        listener = New DBListener(Me, user.HostName)

    End Sub
    Private Sub onSend(sender As Object, e As EventArgs) Handles bt_send.Click
        Dim command As SqlCommand = New SqlCommand
        Dim msgText = tb_text.Text
        Dim reciever As IMSUser = lb_Online.SelectedItem
        Dim insert_string As String = "Insert INTO dbo.IMSMessages(Text,RecieverHost,SenderHost,isRead,isRecieved,Stamp) Values(@Text,@RecieverHost,@SenderHost,@isRead,@isRecieved,@Stamp)"
        Dim adapter As SqlDataAdapter = New SqlDataAdapter
        Dim table As DataTable = New DataTable()
        Try

            If Not sqlConn.State = ConnectionState.Open Then
                sqlConn.Open()

            End If
            command.Connection = sqlConn
            command.CommandText = insert_string
            adapter.SelectCommand = command
            adapter.Fill(table)
            command.CommandText = insert_string
            command.Parameters.Add("@Text", SqlDbType.VarChar).Value = msgText
            command.Parameters.Add("@RecieverHost", SqlDbType.NChar).Value = reciever.HostName
            command.Parameters.Add("@SenderHost", SqlDbType.NChar).Value = user.HostName
            command.Parameters.Add("@isRecieved", SqlDbType.Bit).Value = 0
            command.Parameters.Add("@isRead", SqlDbType.Bit).Value = 0
            command.Parameters.Add("@Stamp", SqlDbType.SmallDateTime).Value = DateTime.Now
            command.ExecuteNonQuery()
        Catch ex As Exception
            Console.WriteLine("onSend: internal database exception" + ex.Message)
        End Try
    End Sub

    Private Sub processMessageId(refMessageID As Integer)
        Dim command As SqlCommand = New SqlCommand
        Dim msgID_string As String = "SELECT * from dbo.IMSMessages " &
                                       "WHERE MessageID=@MessageID AND isRecieved = 0" &
                                       " ORDER BY Stamp"
        Dim isRecievedUpdate_string As String = "Update dbo.IMSMessages " &
                                            "SET isRecieved=1" &
                                             " WHERE MessageID=@MessageID"
        Dim adapter As SqlDataAdapter = New SqlDataAdapter
        Dim table As DataTable = New DataTable()
        Try
            If Not sqlConn.State = ConnectionState.Open Then
                sqlConn.Open()

            End If
            command = New SqlCommand
            command.CommandText = msgID_string
            command.Parameters.Add("@MessageID", SqlDbType.Int).Value = refMessageID
            command.Connection = sqlConn
            adapter.SelectCommand = command
            adapter.Fill(table)
            command = New SqlCommand
            command.Connection = sqlConn
            command.CommandText = isRecievedUpdate_string
            command.Parameters.Add("@MessageID", SqlDbType.Int).Value = refMessageID
            command.ExecuteNonQuery()

        Catch ex As Exception
            Console.WriteLine("processMessageID: internal database exception" + ex.Message)
        End Try
        Try
            Dim row As DataRow = table.Rows(0)
            Dim senderHost As String = row("SenderHost")
            Dim sender As IMSUser = New IMSUser(senderHost)
            Dim currentSheet As IMSSheet
            Dim stringText As String = row("Text")
            Dim stamp As Date = row("Stamp")
            currentSheet = New IMSSheet(sender, stringText, stamp)
            currentSheet.Show()
        Catch ex As Exception
            Console.WriteLine("processMessageID: error while showing sheet")
        End Try
    End Sub

    Public Sub recieveNewMessages(newMessageTable As DataTable)
        For Each row As DataRow In newMessageTable.Rows
            Dim id As Integer = row("MessageID")
            processMessageId(id)
        Next
    End Sub

    Public Sub updateOnlineUserList()
        lb_Online.Items.Clear()
        Dim adapter As SqlDataAdapter = New SqlDataAdapter
        Dim table As DataTable = New DataTable()
        Dim command As SqlCommand = New SqlCommand
        Dim onlineUser_string = "Select * FROM dbo.IMSUser"
        command.CommandText = onlineUser_string
        Try
            If Not sqlConn.State = ConnectionState.Open Then
                sqlConn.Open()

            End If
            command.Connection = sqlConn
            adapter.SelectCommand = command
            adapter.Fill(table)
        Catch ex As Exception
            Console.WriteLine("internal database exception" + ex.Message)
        End Try
        For Each row As DataRow In table.Rows

            Dim tmp As IMSUser = New IMSUser(row("HostName"))
            If Not user.Equals(tmp) Then
                lb_Online.Items.Add(tmp)
            End If
        Next
    End Sub

End Class

IMSSheet 表单:

    Public Class IMSSheet
        Dim IsDraggingForm As Boolean = False
        Private MousePos As New System.Drawing.Point(0, 0)

    Public Sub New(session As IMSUser, text As String, stamp As Date)
        Ini

tializeComponent()
            Me.tb_sender.Text = session.HostName
            addText(text, stamp)
        End Sub

        Private Sub addText(msg As String, stamp As DateTime)
            Dim currentText As String = tb_text.Text
            currentText = currentText + stamp.ToString + Environment.NewLine + msg + Environment.NewLine
            tb_text.Text = currentText
        End Sub
        Private Sub IMSSheet_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseDown
            If e.Button = MouseButtons.Left Then
                IsDraggingForm = True
                MousePos = e.Location
            End If
        End Sub

        Private Sub IMSSheet_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp
            If e.Button = MouseButtons.Left Then IsDraggingForm = False
        End Sub

        Private Sub IMSSheet_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseMove
            If IsDraggingForm Then
                Dim temp As Point = New Point(Me.Location + (e.Location - MousePos))
                Me.Location = temp
                temp = Nothing
            End If
        End Sub

        Private Sub onClose(sender As Object, e As EventArgs) Handles bt_Close.Click
            Me.Close()
        End Sub


        Private Sub bt_minimize_Click(sender As Object, e As EventArgs) Handles bt_minimize.Click
            Me.WindowState = FormWindowState.Minimized
        End Sub

        Private Sub bt_Close_MouseHover(sender As Object, e As EventArgs) Handles bt_Close.MouseHover

        End Sub

        Private Sub bt_minimize_MouseHover(sender As Object, e As EventArgs) Handles bt_minimize.MouseHover

        End Sub
    End Class

有趣的是,如果在我运行程序之前我的 Messagestable 中已经有一些新消息,它们将正确显示。如果我使用 Form.ShowDialog() 显示工作表,它也可以工作,但不是我想要的工作方式。

由于我没有想法,我希望你能帮助我。

4

1 回答 1

0

您遇到了问题,因为更改事件SqlDependency发生在与您的 UI 线程不同的线程上。从SqlDepending 文档

OnChange 事件可能在与启动命令执行的线程不同的线程上生成。

sheet.Show()在非 UI 线程上调用时,Show()将锁定,因为没有消息循环来处理 UI 事件。使用Invoke() on在您的 UI 线程上ImsMain创建和显示。ImsSheet

于 2013-07-16T03:15:20.837 回答