possible of multiple sqldependancy in vb.net? - sql

Basically my code is based on here
http://www.dreamincode.net/forums/topic/185244-using-sqldependency-to-monitor-sql-database-changes/
Current situation is i'm having 2 table wish to monitor so i simple duplicate another similar code with first sqldependancy, but it's failed and seem like the latest sqldependancy will replace the previous sqldependancy function.
here is the code of mine
Public Sub GetNames()
If Not DoesUserHavePermission() Then
Return
End If
lbQueue.Items.Clear()
' You must stop the dependency before starting a new one.
' You must start the dependency when creating a new one.
Dim connectionString As String = GetConnectionString()
SqlDependency.Stop(connectionString)
SqlDependency.Start(connectionString)
Using cn As SqlConnection = New SqlConnection(connectionString)
Using cmd As SqlCommand = cn.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT PatientID FROM dbo.[patient_queue]"
cmd.Notification = Nothing
' creates a new dependency for the SqlCommand
Dim dep As SqlDependency = New SqlDependency(cmd)
' creates an event handler for the notification of data changes in the database
AddHandler dep.OnChange, AddressOf dep_onchange
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read()
lbQueue.Items.Add(dr.GetInt32(0))
doctor.lbqueue.items.add(dr.GetInt32(0))
End While
End Using
End Using
End Using
End Sub
Private Sub dep_onchange(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
' this event is run asynchronously so you will need to invoke to run on the UI thread(if required)
If Me.InvokeRequired Then
lbQueue.BeginInvoke(New MethodInvoker(AddressOf GetNames))
My.Computer.Audio.PlaySystemSound(Media.SystemSounds.Asterisk)
Else
GetNames()
End If
' this will remove the event handler since the dependency is only for a single notification
Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
RemoveHandler dep.OnChange, AddressOf dep_onchange
End Sub
Public Sub GetMedID()
If Not DoesUserHavePermission() Then
Return
End If
lbMedQueue.Items.Clear()
' You must stop the dependency before starting a new one.
' You must start the dependency when creating a new one.
Dim connectionString As String = GetConnectionString()
SqlDependency.Stop(connectionString)
SqlDependency.Start(connectionString)
Using cn As SqlConnection = New SqlConnection(connectionString)
Using cmd As SqlCommand = cn.CreateCommand()
cmd.CommandType = CommandType.Text
cmd.CommandText = "SELECT RecordID FROM dbo.[medicine_queue]"
cmd.Notification = Nothing
' creates a new dependency for the SqlCommand
Dim dep As SqlDependency = New SqlDependency(cmd)
' creates an event handler for the notification of data changes in the database
AddHandler dep.OnChange, AddressOf dep_onchange2
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read()
lbMedQueue.Items.Add(dr.GetInt32(0))
End While
End Using
End Using
End Using
End Sub
Private Sub dep_onchange2(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs)
' this event is run asynchronously so you will need to invoke to run on the UI thread(if required)
If Me.InvokeRequired Then
lbMedQueue.BeginInvoke(New MethodInvoker(AddressOf GetMedID))
My.Computer.Audio.PlaySystemSound(Media.SystemSounds.Asterisk)
Else
GetMedID()
End If
' this will remove the event handler since the dependency is only for a single notification
Dim dep As SqlDependency = DirectCast(sender, SqlDependency)
RemoveHandler dep.OnChange, AddressOf dep_onchange2
End Sub
finally i called GetNames,GetMedID on load form, it worked fine,just GetMedID is functioning and GetNames does not firing event when onchanged.

I think the main problem here is that you are calling .Stop and then .Start within each of your data access methods, thus cancelling and restarting the dependency each time you access the methods.
You just need to call .Start once when your application starts, and similarly .Stop when it ends.
For example, in a web application the best place for this is the Global.asax Application_Start and Application_End events.

I think you are right, I ran into the same issue. A second call to SqlDependency.Start(connectionString), even after a New SqlDependency(cmd), replaced the existing, initial default Service Broker service and queue.
Service Broker creates a default Service and Queue on each Start using the a GUID as part of the Service and Queue names: Service{GUID} and Queue{GUID} - but there seems to only be one defualt Service/Queue pair available
You can verify this by putting a break-point immediately after the first Start and immediately after the second Start. Go to SQL Server, go to your dBase and look at the
Service Broker/Services and Service Broker/Queues folders. You'll need to right-click on the Services and Queues folders and select refresh after the second break-point

Related

Freeze UI with Threads

I have a timer to make a thread:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'ListBox2.Items.Add("Hilo")
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
End Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
cmd.Connection.Open()
da.SelectCommand = cmd
da.Fill(dataSet, "Query")
For Each fila As DataRow In dataSet.Tables(0).Rows
cmd = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (#docEntry) AND TIPO = (#tipodoc)", conn)
cmd.Parameters.AddWithValue("#docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("#tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
Next
cmd.Connection.Close()
dataSet.Tables("Query").Clear()
End If
End Sub
The timer have a 4000 interval, but when a thread start freeze my UI, I think is because the process is so big or the querys but i need make it without freezing.
The comment is correct, and I will describe for you the issues hinted therein
Using a System.Windows.Forms.Timer
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
This will run on the UI thread, and is only appropriate when doing UI things (even then, I am not sure you can really make a case for it over System.Threading.Timer)
Creating a new System.Threading.Thread
hiloCertificador1 = New Thread(AddressOf crearObjeto1)
hiloCertificador1.IsBackground = True
hiloCertificador1.Start()
This now runs off the UI, and is the entire contents of the Timer.Tick. So you've ticked on the UI then created a new thread off the UI. This is very odd
Calling a Sub to call a Sub
Public Sub crearObjeto1()
UpdateList()
End Sub
Private Sub UpdateList()
' do stuff
End Sub
The redundancy should be self-evident
Doing non-UI stuff but following the Control.InvokeRequired/BeginInvoke pattern
Private Delegate Sub UpdateListDelegate()
Private Sub UpdateList()
If Me.InvokeRequired Then
Me.BeginInvoke(New UpdateListDelegate(AddressOf UpdateList))
Else
' looks like a bunch of non-UI stuff
End If
End Sub
This pattern is used for doing things on the UI but there seems to be no UI code in that block.
Not using Using to ensure proper disposal of IDisposable objects
Dim conn As New SqlConnection(parametrosCon)
Dim cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Dim da As New SqlClient.SqlDataAdapter(cmd)
' do stuff
cmd.Connection.Close()
DataSet.Tables("Query").Clear()
Nongermane to your current issue, but also important to know.
Solution
So although it looks like a noble effort, you seem to be going back and forth between UI and not for no reason at all, or more accurately, creating issues where none existed with some over-engineering. The whole thing can be simplified with some minor changes
Use System.Threading.Timer
Dim Timer2 As New System.Threading.Timer(Sub() UpdateList(), Nothing, -1, -1)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' done in Form_Load if your Timer1 is Enabled in designer
' or can be done in a Button.Click, or however you had enabled Timer1
Timer2.Change(2000, 4000) ' this will enable Timer after 2 seconds, then will tick every 4 seconds
'Timer2.Change(-1, -1) ' this is how it's disabled
End Sub
Just call this method, and use Using to properly dispose your database objects. Added a Sub DoUiStuff() which is how the pattern is properly implemented
Private Sub UpdateList()
Timer2.Change(-1, -1)
Using conn As New SqlConnection(parametrosCon)
conn.Open()
Using cmd = New SqlCommand("SELECT TOP 1 * FROM COLA WHERE docentry < 8654 and enviado = 0", conn)
Using da As New SqlClient.SqlDataAdapter(cmd)
da.SelectCommand = cmd
da.Fill(DataSet, "Query")
For Each fila As DataRow In DataSet.Tables(0).Rows
Using cmdInner = New SqlCommand("UPDATE COLA SET enviado = 1 WHERE DOCENTRY = (#docEntry) AND TIPO = (#tipodoc)", conn)
cmd.Connection.Open()
cmd.Parameters.AddWithValue("#docEntry", fila("docentry"))
cmd.Parameters.AddWithValue("#tipodoc", fila("tipo"))
cmd.ExecuteNonQuery()
Dim factura As New FacturaCerificacion(fila("docentry"), fila("tipo"))
End Using
Next
End Using
End Using
DoUiStuff(arguments) ' for example, if you need to update a GridView
DataSet.Tables("Query").Clear()
End Using
End Sub
Private Sub DoUiStuff(arguments As Whatever)
If Me.InvokeRequired() Then
Me.Invoke(New Action(Of Whatever)(AddressOf DoUiStuff), arguments)
Else
' do UI stuff with arguments
End If
Timer2.Change(2000, -1)
End Sub
Finally, so I'm not contradicting myself, I'll add the Dispose method to dispose of the Timer. This Sub will be by default in your Form.Designer.vb file which you can leave there or move it to Form.vb once you add to it.
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing Then
components?.Dispose()
Timer2?.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub

How to properly set up BackgroundWorker in WPF VB

I am working in VB and when the end user clicks on a button, I want to show a “Please wait” window (no progress update necessary) and trigger the process in the background on another thread as it might take a while to complete. I understand BackgroundWorker is the recommended approach for this, and although I have previously set up a BGW on a windows forms application, I am new to WPF and having a hard time understanding the differences.
In order to reuse some of my code, I have written a function (“DbOperation”) that executes a SQL stored procedure (from parameter) and returns a data table (or NULL if no output), and it seems to work well everywhere except in concert with my BGW.
I understand UI should only be affected by the main thread, and background threads should not touch the UI, and the BGW thread should call the long-running process.
Private Sub btnProcessReports_Click(sender As Object, e As RoutedEventArgs) Handles btnProcessReports.Click
'Set up background worker
bgw.WorkerReportsProgress = False
bgw.WorkerSupportsCancellation = False
AddHandler bgw.DoWork, AddressOf bgw_DoWork
AddHandler bgw.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted
'Show please wait window
f.Label1.Text = "Importing web reports. Please wait."
f.Show()
'Start the work!
bgw.RunWorkerAsync()
End Sub
Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs)
'Set current thread as STA
Thread.CurrentThread.SetApartmentState(ApartmentState.STA)
'Trigger job
Dim Qry As String = "EXEC [msdb].[dbo].sp_start_job N'Import Online Payment Portal Reports';"
DbOperation(Qry)
End Sub
Public Function DbOperation(ByVal qry As String, Optional ByVal type As DatabaseQueryReturnType = DatabaseQueryReturnType.NonQuery) As Object
Dim objDB As New Object
Dim dt As DataTable
Dim da As SqlDataAdapter
OpenConnection()
Dim cmd As New SqlCommand(qry)
cmd.Connection = con
If type = DatabaseQueryReturnType.DataTable Then
dt = New DataTable
da = New SqlDataAdapter(cmd)
Try
da.Fill(dt)
Catch ex As Exception
MessageBox.Show("Error retrieving data from database: " & ex.Message.ToString)
End Try
objDB = dt
dt.Dispose()
CloseConnection()
Return objDB
ElseIf type = DatabaseQueryReturnType.NonQuery Then
Try
cmd.ExecuteNonQuery()
Catch ex As Exception
MessageBox.Show("Error executing nonquery: " & ex.Message.ToString)
End Try
objDB = Nothing
CloseConnection()
Return objDB
End If
Return objDB
End Function
When I try running the program, it gives me “The calling thread must be STA, because many UI components require this.” on the line that calls my function. After a bit of research, I learned that you have to call SetApartmentState() before the thread is started, but this doesn’t make sense since the recommended code seems to be:
Thread.CurrentThread.SetApartmentState(ApartmentState.STA)
After adding this line, I then get “Failed to set the specified COM apartment state” on that line of code.
I have also tried commenting out the messagebox lines in the function in case it was trying to open the messageboxes on the UI thread, but it had no impact and I still got the same errors.
What am I missing in my code to get the function to run on the BGW thread?

Cannot cancel a background worker running a SQL query

I have searched SO and Google but I cannot find the information I am seeking which may indicate I am not using Background Workers correctly. In my case, I am running a SQL query in a Background Worker so the GUI does not freeze for the user. Please note, supportscancellation is set to true.
However, I am trying to stop the background worker if the user wants to cancel the query. Because my background worker does not have a loop, I am unsure how to accomplish this.
My background worker simply looks at a boolean to determine the type of SQL query, then makes a call to another event to run it.
Sub BackgroundWorker1DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
If Psearch = True Then
P_Search()
End If
If Asearch = True Then
A_Search()
End If
If Msearch = True Then
M_Search()
End If
If Dsearch = True Then
D_Search()
End If
End Sub
Based on articles I find, it seems adding an if statement to check for cancellationpending is irrelevant as it would only fire the one time (at the start of the RunWorkerAsync). So, I have tried using a For/Do loop to check for cancellationpending or workercompleted but it still will not cancel and dispose of the background worker. I have validated this by attempting to restart the background worker but I am erroring out due to workser isbusy.
Sub Button7Click(sender As Object, e As EventArgs)
If backgroundWorker1.IsBusy() Then
backgroundWorker1.CancelAsync()
backgroundWorker1.Dispose()
End If
End Sub
Sub D_Search()
Dim sqlConnection1 As New SqlConnection(GlobalConString)
Dim cmd As New SqlCommand
cmd.CommandText = RealQuery
cmd.CommandTimeout = 0
cmd.Connection = sqlConnection1
sqlConnection1.Open()
Dim daQuery As New System.Data.SqlClient.SqlDataAdapter(cmd)
Dim dsQuery As New DataSet
Try
If backgroundWorker1.CancellationPending = False
daQuery = New System.Data.SqlClient.SqlDataAdapter
daQuery.SelectCommand = cmd
daQuery.Fill(dsQuery, "Query")
tblQuery = dsQuery.Tables("Query")
Else
backgroundWorker1.Dispose()
sqlConnection1.Close()
End If
Catch Err As System.Exception
MessageBox.Show(Err.ToString)
Finally
sqlConnection1.Close()
End Try
End Sub

When deleting connecting from sql server all connections are deleted not just the one i want

In vb,net i have some code to remove selected user connections from a sql server database.
At present when i open the page it deletes all user connections, this should not happen. I should open the page and click the delete button on the table row then it removes the connection.
I'm working in Visual Studio with vb.net
Please see my code and offer advice
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim dtTable As DataTable = Nothing
Dim intConnectionId As Integer = 0
If IsPostBack Then
intConnectionId = CInt(result.Value)
' Get user data from session
With HttpContext.Current
End With
End If
Using sqlCommand As New SqlCommand
With sqlCommand
.CommandText = "usp_connectsdetailedlist"
.CommandType = CommandType.StoredProcedure
' Remove connection
modConnections.Delete(intConnectionId)
' Clear desks attahced to connection
modDeskText.ClearUserDesk(intConnectionId)
' Send message to server to delete connection
modMessages.Send(ccsStdCallBackCon.scbcDisconnect, intConnectionId, True)
End With
Execute(sqlCommand, dtTable)
End Using
With Rep1
.DataSource = dtTable.DefaultView
.DataBind()
End With
If Not IsNothing(dtTable) Then
dtTable.Dispose()
End If
End Sub
The problem is you written delete connection code in page_Load, so
it is executing whenever the page loads.
Try to add a button and write the same code in Button_Click event.

Compare two datagridview and remove rows dependent on row properties

I am currently working in vb.net windows form applications. I have two DataGridViews and I want to cross reference the rows and remove certain rows out of datagridview1, depending on if a checkbox is checked in datagridview2. This problem arises when I check a box for an action taken by a front end user in datagridview1 that updates datagridview2. But, when I refresh both DataGridViews, datagridview2 shows that this action has been taken, but the checkbox column in datagridview1 goes back to unchecked, thus telling the front end user to repeat this action.
On a side note all the data is bound to an sql table, except the checkbox column in datagridview1. Also note, I have a refresh button that repeats the load event to refresh both tables.
Page Load Event Handler:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'load datagridview1'
Dim ds As New DataSet
Dim AA As New DataSet
connectionstring = "Data source = .\sqlexpress; integrated security = true"
connection = New SqlConnection(connectionstring)
sql = "SELECT Shear FROM production.dbo.stagingcompleted"
Try
connection.Open()
adapter = New SqlDataAdapter(sql, connectionstring)
adapter.Fill(ds)
connection.Close()
DataGridView1.DataSource = ds.Tables(0)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
'load datagridview2'
connectionstring = "Data source = .\sqlexpress; integrated security = true"
connection = New SqlConnection(connectionstring)
sql = "SELECT * FROM production.dbo.tblFCOrdered"
Try
connection.Open()
adapter = New SqlDataAdapter(sql, connectionstring)
adapter.Fill(AA)
connection.Close()
DataGridView2.DataSource = AA.Tables(0)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
DataGridView1 Event Handler (CellContentClick):
Private Sub DataGridView1_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
'update datagridview1 to the action for that row complete'
If e.ColumnIndex <> 0 Then
Exit Sub
End If
Dim v As String = DataGridView1.Rows(e.RowIndex).Cells(1).Value
Select Case MsgBox("Are you sure shear " & v & " has been cut?", MsgBoxStyle.YesNo)
Case MsgBoxResult.No
Exit Sub
Case MsgBoxResult.Yes
Try
Dim connstring = "Data Source=.\sqlexpress; integrated security = true"
Using conn1 As New SqlConnection(connstring)
conn1.Open()
Using comm1 As SqlCommand = New SqlCommand("UPDATE Production.dbo.tblFCOrdered SET FormChannel = 1 WHERE SHEAR = '" & v & "'", conn1)
comm1.ExecuteNonQuery()
conn1.Close()
End Using
End Using
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Select
I am possibly incorrect and I apologise if you have already tried this. I believe you might benefit from reading up about the page life-cycle, order event handlers and the effects of PostBack (for example this article)
In server controls, certain events, typically click events, cause the page to be posted back immediately to the server. Change events in HTML server controls and Web server controls, such as the TextBox control, do not immediately cause a post. Instead, they are raised the next time a post occurs.
In regards to PostBack and Page.Load:
After a page has been posted back, the page's initialization events (Page_Init and Page_Load) are raised, and then control events are processed. You should not create application logic that relies on the change events being raised in a specific order unless you have detailed knowledge of page event processing.
I think that part of the problem you are experiencing is that the first thing the page will do when you trigger a PostBack, e.g. by clicking on a cell, is the code which you have in your Page.Load event handler. In your code you will find that when the page is processed (on PostBack) the first thing that happens is that the grids are data bound. Because of this any changes made will not be persisted when the other event handlers are called. A common check before performing actions such as data binding in the Page.Load function is to check that the page is not being posted back, if IsPostBack = true it is likely that you will want to skip such actions.