Using and Try/Catch blocks - vb.net

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

Related

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

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

using loop to hide ribbon controls vb.net

I have a table for user screens.
UserID - ScreenID - Perm
I need to hide controls when the form opens with UserID and ScreenID, and I can't use the loop with condition.
This is my code:
Try
Dim da As New SqlDataAdapter
Dim ds As New DataSet
If dbconnect.State = ConnectionState.Closed Then dbconnect.Open()
da = New SqlDataAdapter("SELECT * FROM UserScreens WHERE UserID='" & UserID & "'", dbconnect)
da.Fill(ds, "Table")
If ds.Tables(0).Rows.Count > 0 Then
Dim M As DataRow = ds.Tables(0).Rows(0)
For Each ctrl As Control In Ribbon1.Controls
ctrl.Visible = M.Item("Perm")
Next
'DocIDtxt.Text = M.Item("DOCID")
End If
Catch ex As Exception
If dbconnect.State = ConnectionState.Open Then dbconnect.Close()
MsgBox(ex.ToString)
End Try
I always worry about connections first. From MS docs
"The IDbConnection object associated with the select command must be valid, but it does not need to be open. If the IDbConnection is closed before Fill is called, it is opened to retrieve data, then closed. If the connection is open before Fill is called, it remains open."
What that means is you opened your connection before the .Fill method so it will remain open. Bad! The only way your code closes the connection is if there is an error. A Finally section with .Close in your Try...End Try would do the trick but better yet use Using...End Using. This block will ensure that your objects are closed and disposed properly even if there is an error. When an object has a Dispose method it may have unmanaged resources that it must clean up.
Next, turn on Option Strict. It will be a great help pointing potential runtime errors.
Third, always use Parameters.
See inline explanation and comments.
Private Sub OpCode()
Try
'A DataTable does not have a Dispose() method so it will fall
'out of scope and be garbage collected.
Dim dt As New DataTable
'SqlConnection has a Dispose() method so use Using
Using cn As New SqlConnection("Your connection string")
'SqlCommand has a Dispose() method so use Using
Using cmd As New SqlCommand("SELECT * FROM UserScreens WHERE UserID= #UserID", cn)
'Always use Parameters. Never concatenate strings in SQL statements.
cmd.Parameters.Add("#UserID", SqlDbType.VarChar).Value = UserID
cn.Open()
'SqlDataReader has a Dispose() method so use Using
Using dr As SqlDataReader = cmd.ExecuteReader
dt.Load(dr)
End Using
End Using
End Using
If dt.Rows.Count > 0 Then
Dim M As DataRow = dt.Rows(0)
For Each ctrl As Control In Ribbon1.Controls
'If Perm is not a boolean this line of code can't work
ctrl.Visible = CBool(M.Item("Perm"))
Next
'DocIDtxt.Text = M.Item("DOCID")
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub

Disposable class keeps reference

I'm using simple DataReader commands very often in my project.
To simplify it, I've created a function:
Public Function DataReaderFromCommand(ByRef uCn As SQLite.SQLiteConnection, ByVal uCommandText As String) As SQLite.SQLiteDataReader
Dim nCmdSel As SQLite.SQLiteCommand = uCn.CreateCommand
With nCmdSel
.CommandText = uCommandText
End With
Dim r As SQLite.SQLiteDataReader = nCmdSel.ExecuteReader
Return r
End Function
In my project I use it like this:
Using r As SQLite.SQLiteDataReader = DataReaderFromCommand(cnUser, "SELECT * FROM settings")
Do While r.Read
'do something
Loop
End Using'this should close the DataReader
In one case, I need to delete my database. However this fails with the error "File is locked by another process".
I tried to isolate the problem, and the locking occurs because of the function "DataReaderFromCommand".
Does anybody see what I'm doing wrong / what keeps the database locked?
I thought that after "End Using" of the datareader, the SQLiteCommand would also be disposed, so there are no further reference to the database.
You should probably be trying to do it this way:
Public Sub UsingDataReader(ByVal connectionString As String, ByVal commandText As String, ByVal action As Action(Of SQLite.SQLiteDataReader))
Using connection As New SQLite.SQLiteConnection(connectionString)
Using command As New SQLite.SQLiteCommand(commandText, connection)
Using reader = command.ExecuteReader()
action(reader)
End Using
End Using
End Using
End Sub
Then you can call the code like this:
UsingDataReader("/* your connection string here */", "SELECT * FROM settings", _
Sub (r)
Do While r.Read
'do something
Loop
End Sub)
This ensures that all of the disposable references are closed when the Sub has completed.
The first problem is that not all the disposables are being disposed of. We are assured that the connection passed to that helper is in a Using block, but the command also needs to be disposed of as it has a reference to the connection:
Dim cmd As New SQLiteCommand(sql, dbcon)
Even if you dont use the overloaded constructor, in order to work, somewhere you set the connection property. This illustrates one of the problems with such "DB helper" methods: the DBConnection, DBCommand and DBReader objects work together very closely, but they are created in different methods with different scopes and you can't normally see if everything is being cleaned up properly.
The code posted will always fail because that DBCommand object - and by extension the DBConnection - are not disposed. But even if you clean up properly, pooling will keep the DBConnection alive for a while as jmcilhinney explains. Here are 2 fixes:
Clear the Pool
Using dbcon As New SQLiteConnection(LiteConnStr),
cmd As New SQLiteCommand(sql, dbcon)
dbcon.Open()
Dim n As Int32 = 0
Using rdr = cmd.ExecuteReader
While rdr.Read
' == DoSomething()
Console.WriteLine("{0} == {1}", n, rdr.GetString(0))
n += 1
End While
End Using
' Clears the connection pool associated with the connection.
' Any other active connections using the same database file will be
' discarded instead of returned to the pool when they are closed.
SQLiteConnection.ClearPool(dbcon)
End Using
File.Delete(sqlFile)
The dbCon and cmd objects are "stacked" into one Using statement to reduce indentation.
This will close and discard any and all connections in the pool, provided they have been Disposed - as well as any objects which reference them. If you use Dim cmd ... you will need to explicitly dispose of it.
Force Garbage Collection
I think this is much more ham-fisted, but it is included for completeness.
Using dbcon As New SQLiteConnection(LiteConnStr),
cmd As New SQLiteCommand(sql, dbcon)
...
Using rdr = cmd.ExecuteReader
...
End Using
End Using
GC.WaitForPendingFinalizers()
File.Delete(sqlFile)
This also works as long as everything has been properly disposed of. I prefer not to mess with GC unless absolutely necessary. The issue here is that clean up will not be limited to DBProvider objects but anything which has been disposed and is awaiting GC.
Yet a third workaround would be to turn off pooling, but you would still have to dispose of everything.
You are going to need to also close your cnUser connection to the database.
Closing/disposing the reader does not necessarily close/dispose the open connection.

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