In vb.net project, how to check if SQL connection is still available and end all processing? - vb.net

I have a problem in a vb.net Windows Form project. My company is having network issues and is losing connection between the Windows Form application and the SQL server. When this happens, the application locks up and closes. Before any SQL commands are executed, is there a way to check if the SQL connection is even available?
I've tried using this:
If (cmd.Connection.State <> ConnectionState.Closed)
after the cmd is setup like this:
Dim cmd As SqlCommand
cmd = MSSQL_CONN.CreateCommand
but the state is often Open because the network connection fails after the SQL command was initialized.
I've tried to catch the error like this:
Private m_conn As SqlConnection
Try
m_conn = New SqlConnection(cn_str)
Call m_conn.Open()
Catch e As Exception
MessageBox.Show("Error")
If MSSQL_CONN.TransactionStarted Then
MSSQL_CONN.RollbackTransaction()
End If
End Try
but this has problems because it's trying to do a rollback for any other errors and the rollback throws an error because of the loss of connection state. This also starts a cascade into other timers and background processes that continue to run and try to connect to the SQL server.
I'm hoping there something like
If *SQL connection is even still available* Then
Call m_conn.Open()
Else
*Don't execute any other SQL on this form or any other background forms*
End If

Normally it's best to use a using statement for a connection where the connection is scoped to the method you are working with the database. Otherwise you could create the connection as needed e.g. check the connection state as on error.
Public Class DataOperations
Public Shared Async Function Connection() As Task(Of SqlConnection)
Dim cn As New SqlConnection With {.ConnectionString = "TODO"}
Try
Await cn.OpenAsync()
Return cn
Catch ex As Exception
Return New SqlConnection
End Try
End Function
End Class
Then there is locally scoped where the choice is OpenAsync or Open.
Public Shared Function GetCatagories() As DataTable
Dim dt As New DataTable
Using cn As New SqlConnection With {.ConnectionString = "TODO"}
Using cmd As New SqlCommand With {.Connection = cn}
cmd.CommandText = "TODO"
Try
cn.Open()
dt.Load(cmd.ExecuteReader())
Catch ex As Exception
' decide on how to handle
End Try
End Using
End Using
Return dt
End Function
EDIT:
Public Shared Async Function IsServerConnected() As Task(Of Boolean)
Using cn As New SqlConnection With {.ConnectionString = ConnectionString}
Try
Await cn.OpenAsync()
Return True
Catch ex As Exception
Return False
End Try
End Using
End Function

Related

Using and Try/Catch blocks

I'm not very experienced with vb and have inherited an application from a previous programmer. I was asked to run the Visual Studio analyse against the code to make changes as the application seemed to be causing memory issues. I got loads of messages about disposing of an object multiple times eg
Warning CA2202 Object 'myConnection' can be disposed more than once in method xxx'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object
Simple, wrap it in a using block I thought. Then I discovered myConnection was used in a try/catch
JumpUpAgain:
Try
myConnection.Open()
Catch ex As Exception
LogIt("EXCEPTION", "Connection Error: " & ex.Message)
myConnection.Close()
SqlConnection.ClearAllPools()
ConnectionString = Nothing
conn = Nothing
MySQLCmd = Nothing
myConnection = Nothing
Threading.Thread.Sleep(3000)
If ErrorLoopCounter = 5 Then
LogItDetail("Exception", "Database Connection Process failed after 5 re-tries")
If Not FailedFiles.Contains(InputFileName) Then FailedFiles.Add(InputFileName)
LogItDetail("DEBUG", "Added file to Failed Files for email")
FileProcessFailed = True
Throw
Else
ErrorLoopCounter += 1
End If
GoTo JumpUpAgain
End Try
And the myConnection object is required in later code.
I'll be taking out the myConnection=Nothing from the catch but if I put the using/end using outside of this try/catch block will it be disposed of correctly if the application hits the Throw?
The Using blocks ensure that your db objects are closed and disposed (equally as important as closing) even if there is an error. You kind of have to fit in the Try Catch since variables defined inside blocks are not visible outside the block.
Private Sub OPCode2()
Dim dt As New DataTable()
'Keep your connections local
Using cn As New MySqlConnection("Your Connection String")
Using cmd As New MySqlCommand("Select * From YourTable", cn)
Try
cn.Open()
Using dr As MySqlDataReader = cmd.ExecuteReader
dt.Load(dr) 'or whatever you want to do with your connection
End Using
Catch
'Your logging logic here
End Try
End Using
End Using
End Sub

Shows connection is closed even if connection.open is present - Works when I remove Try Block

This is the function to access the database; the connection string is perfect - There is another Function similar to this and it works fine.
Friend Shared Function AddMember(member As Object) As Task(Of Integer)
Dim connection = New SqlConnection(Configuration.ConfigurationManager.ConnectionStrings("Carrel").ConnectionString)
Dim query = New SqlCommand("INSERT INTO JSON (CATEGORY,DATA) VALUES ('MEM',#JSONString)", connection)
Try
Connection.Open()
query.Parameters.Add(New SqlParameter("#JSONString", JsonConvert.SerializeObject(member)))
Return query.ExecuteNonQueryAsync()
Catch ex As Exception
MsgBox(ex.ToString())
Throw
Finally
query.Connection.Close()
End Try
End Function
Try this:
Friend Shared Async Function AddMember(member As Object) As Task(Of Integer)
Dim cn AS New SqlConnection(Configuration.ConfigurationManager.ConnectionStrings("Carrel").ConnectionString)
Dim query As New SqlCommand("INSERT INTO JSON (CATEGORY,DATA) VALUES ('MEM',#JSONString)", cn)
Try
cn.Open()
query.Parameters.Add(New SqlParameter("#JSONString", JsonConvert.SerializeObject(member)))
Return Await query.ExecuteNonQueryAsync()
Catch ex As Exception
MsgBox(ex.ToString())
Throw
Finally
query.Connection.Close()
End Try
End Function
This should prevent the method from continuing past the ExecuteNonQueryAsync() line until the task returns, so the connection will stay open, but can still run without blocking.
It's also better practice to put the connection and command object in Using blocks, and to not worry about exceptions at this level. This is a data access method... separate that from your presentation code. Let the exception bubble up in the calling code, especially in an Async method:
Friend Shared Async Function AddMember(member As Object) As Task(Of Integer)
Using cn AS New SqlConnection(Configuration.ConfigurationManager.ConnectionStrings("Carrel").ConnectionString), _
cmd As New SqlCommand("INSERT INTO JSON (CATEGORY,DATA) VALUES ('MEM',#JSONString)", cn)
query.Parameters.Add(New SqlParameter("#JSONString", JsonConvert.SerializeObject(member)))
cn.Open()
Return Await query.ExecuteNonQueryAsync()
End Using
End Function
Just be careful, because the async code could be hiding exceptions from you now.
The problem is that, as Aman Bachas said, you're calling ExecuteNonQueryAsync which will start the operation in the background. But then your Finally block will execute and close the connection before the execute actually happens.
What you'll need to do is call the non-Async version but then do your own Async so you can close the connection after it's done.
Public Function AddMember(member As Object) As Threading.Tasks.Task(Of Integer)
Dim connection = New SqlConnection(Configuration.ConfigurationManager.ConnectionStrings("Carrel").ConnectionString)
Dim query = New SqlCommand("INSERT INTO JSON (CATEGORY,DATA) VALUES ('MEM',#JSONString)", connection)
Try
connection.Open()
query.Parameters.Add(New SqlParameter("#JSONString", JsonConvert.SerializeObject(member)))
Return Threading.Tasks.Task.Factory.StartNew(Of Integer)(Function()
Dim ret = query.ExecuteNonQuery()
connection.Close()
Return ret
End Function)
Catch ex As Exception
MsgBox(ex.ToString())
Throw
End Try
End Function
You'll probably want to add some error handling inside the task and clean this up a bit, but that should give you a starting point.
Please replace ExecuteNonQueryAsync() with the ExecuteNonQuery().

How to close database connection?

I'm having a problem in project of mine in VB.NET. The problem is whenever I want to save, delete or update data with an Access database I get an error with a message saying that "not allowed to change the connection string property. connection's current state is open".
I have used If con.State = ConnectionState.Open Then con.Close() End If
command using finally in every section where I have called the database.
But still I'm having the same problem. What am I doing wrong?
Use the "USING"-Keyword. Exiting a using block calls .Dispose() on the object which for a SqlConnection will close the connection and any open resources.
Using connection As New SqlConnection(connection)
Dim command As New SqlCommand("Select * From dbo.table1",connection)
command.ExecuteNonQuery()
End Using
EDIT:
Module Module1
Public Sub DbConnection()
Dim connectionString as String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=yourServerAddress;Initial Catalog=university.mdb;
Integrated Security=SSPI;"
Using connection as New Sqlconnection(connectionstring)
Dim command As New SqlCommand("Select * From dbo.table1",connection)
command.ExecuteNonQuery()
End Using
End Sub
End Module

Returning From a Function BEFORE disposing sqlconnection

I should begin by stating that I assume that the answer to this question is NO, but I wanted to ask to be sure.....If you open a sqlconnection inside of a function(with a USING block), and return from that function prior to reaching the end of the block, will that connection be disposed of properly?
For Example:
Public Function Myfunction() As Boolean
Dim ConnectionString As String = "connectionstring goes here"
Dim sql As String = "SELECT * FROM mytable"
Using connection As New SqlClient.SqlConnection(ConnectionString)
Using command As New SqlClient.SqlCommand(sql, connection)
connection.Open()
Using reader As SqlClient.SqlDataReader = command.ExecuteReader
While reader.Read
Return True
End While
End Using
End Using
End Using
Return False
End Function
Will the above connection be disposed of properly if the return true line is executed?
If you open a sqlconnection inside of a function(with a USING block), and return from that function prior to reaching the end of the block, will that connection be disposed of properly?
Yes. Absolutely. That's the beauty of a USING block. See
A Using block behaves like a Try...Finally construction in which the Try block uses the resources and the Finally block disposes of them. Because of this, the Using block guarantees disposal of the resources, no matter how you exit the block. This is true even in the case of an unhandled exception, except for a StackOverflowException
https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/using-statement

vb.net polling Sql server once a second causing timeout

I was asked to do a system that would poll one table in the database every second and if it counters a row that meets a criteria start actions to handle that.
I've done this but every now and then I get a time out exception. I have a WPF application where I have a thread that runs in background. This thread has a loop and sleeps for one second at the end of the loop. The connection to the database is opened inside "using" clause.
Below is my thread sub:
Private Sub PollDatabase()
While m_StopThread = False
Try
Dim listOfRows As List(Of DataObject) = db.GetDataObjects()
... Do something with the rows ...
Catch ex As Exception
m_log.WriteLine(ex.ToString())
End Try
Thread.Sleep(1000)
End While
End Sub
And my SQL function looks like this:
Public Function GetDataObjects() As List(Of DataObject)
Dim result As New List(Of DataObject)
Dim sb As New StringBuilder("... the sql query ...")
Using cnn = New SqlConnection(_connectionString)
cnn.Open()
Using cmd = New SqlCommand(sb.ToString(), cnn)
cmd.CommandTimeout = 0
Using DataReader As SqlDataReader = cmd.ExecuteReader()
Do While DataReader.Read()
... read the columns from table
to the dataobject ...
result.Add(DataObject)
Loop
End Using
End Using
End Using
Return result
End Function
Now what seems randomly my log has a time out exception:
System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
...
at System.Data.SqlClient.SqlConnection.Open()
My questions are: is this at all save way of doing this? Or am I doing something fundamentally wrong here? And of course if anyone have a suggestion to fix this issue.
EDIT:
I tried a bit different approach with the SQL function. I'm now opening a connection once when my application starts and dumped the "using" clauses. So my function looks something like this now:
Public Function GetDataObjects() As List(Of DataObject)
Dim result As New List(Of DataObject)
Dim sb As New StringBuilder("... the sql query ...")
_sqlCmd.CommandText = sb.ToString()
Using DataReader As SqlDataReader = _sqlCmd.ExecuteReader()
Do While DataReader.Read()
... fill the list with objects ...
Loop
End Using
Return result
End Function
My log is clean form errors. So is there something wrong opening a connection to the server once in a second as I do with the using?
EDIT:
I've done a lot of testing now to identify the problem. What I discovered is that just connecting multiple times to the server doesn't cause any problems. Neither does adding a select statement after the connection. But when I actually implement a function where is the complete reader part and return my results I run into the time out problems. Here is two examples.
This isn't causing issues:
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
Me.DataContext = Me
m_Thread = New Thread(AddressOf ConnectionTestFunction)
m_Thread.IsBackground = True
m_Thread.Start()
End Sub
Private Sub ConnectionTestFunction()
While m_stopThread = False
Try
m_log.WriteLine("GetData (" & m_ThreadCounter & ")")
Using cnn As SqlConnection = New SqlConnection("Data Source=server;Initial Catalog=db;Integrated Security=True;MultipleActiveResultSets=True")
cnn.Open()
Using cmd As SqlCommand = New SqlCommand("SELECT * FROM Data", cnn)
Using DataReader As SqlDataReader = cmd.ExecuteReader()
Do While DataReader.Read()
Loop
End Using
End Using
End Using
Catch ex As Exception
m_log.WriteLine(ex.ToString())
End Try
m_ThreadCounter += 1
Thread.Sleep(1000)
End While
End Sub
This is causing timeout errors:
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
Me.DataContext = Me
m_Thread = New Thread(AddressOf ConnectionTestFunction)
m_Thread.IsBackground = True
m_Thread.Start()
End Sub
Private Sub ConnectionTestFunction()
While m_stopThread = False
Try
m_log.WriteLine("GetData (" & m_ThreadCounter & ")")
Dim datarows As List(Of Data) = Me.GetData()
Catch ex As Exception
m_log.WriteLine(ex.ToString())
End Try
m_ThreadCounter += 1
Thread.Sleep(1000)
End While
End Sub
Private Function GetData() As List(Of Data)
Dim result As New List(Of Data)
Using cnn As SqlConnection = New SqlConnection("Data Source=server;Initial Catalog=db;Integrated Security=True;MultipleActiveResultSets=True")
cnn.Open()
Using cmd As SqlCommand = New SqlCommand("SELECT * FROM Data", cnn)
Using DataReader As SqlDataReader = cmd.ExecuteReader()
Do While DataReader.Read()
Dim d As New Data()
d.DataId = DataReader("DataId")
... etc fields about 10 of them ...
result.Add(d)
Loop
End Using
End Using
End Using
Return result
End Function
I'm really happy if anyone have any thoughts about this... I have to admit I'm really confused now.
Probably, your code is taking longer to complete than the default time-out value for the connection is. Try specifying the time-out when you create your Sql Connection. Make sure that it's longer than the time your code needs to complete.
This approach doesn't seem very good.. Instead of pooling, why not react when new data comes? You can use a trigger or SqlDependency?
http://dotnet.dzone.com/articles/c-sqldependency-monitoring