I'm trying to find out what is the best way to create a class so I can use it to connect to the SQL Server and be able to rollback everything if a single error exists.
Right now, I'm using a single connection for a procedure. This, sometimes, is causing my code to throw There is already an open DataReader associated with this Command which must be closed first.
I thought of setting up a new connection for every transaction but I don't know if I can rollback everything if I do it that way.
My current "way", if a single error exists, I call sBDCloseConnect(True)
and it does a rollback and closes the connection. This is useful because I usually do a lot iteration with multiple sql queries.
Below is the relevant code I use to do this and I would like to know if there is a better way to do this and avoid the error thrown I mentioned previously.
One last note, I don't want a solution like MARS.
Test case
'' Initialize connection to database
sBDInitConnect()
Dim test1 As Integer = Val(sBDReturnQuery("SELECT 1"))
If test1 < 0 Then
'' Has thrown an error
sBDCloseConnect(True)
ElseIf test1 > 1 Then
Using dt As New DataTable
Dim test2 As Integer = sBDSelectSQL("SELECT name FROM users WHERE idUser=1", dt)
If test2 < 0 Then
'' Has thrown an error
sBDCloseConnect(True)
ElseIf test2 > 0 Then
If Not sBDNonQuery("UPDATE users SET name='only a test' WHERE idUser=1") Then
sBDCloseConnect(True)
Else
If Not sBDNonQuery("UPDATE users SET name2='this field does not exist' WHERE idUser=1") Then
sBDCloseConnect(True)
End If
End If
End If
End Using
End If
'' No error
sBDCloseConnect(False)
Class
Public Partial Class componenteDEFAULT
Inherits System.Web.UI.UserControl
Private bdConection As Data.SqlClient.sqlConnection
Private bdTransaction As Data.SqlClient.sqlTransaction
Public bdCommand As Data.SqlClient.SqlCommand
Sub sBDInitConnect()
Try : bdConection.Close() : Catch : End Try
Try
bdConection = Nothing
bdConection = New Data.SqlClient.SqlConnection(bd.ConnectString)
bdConection.Open()
bdTransaction = Nothing
bdTransaction = bdConection.BeginTransaction(IsolationLevel.ReadCommitted)
bdCommand = New Data.SqlClient.SqlCommand()
bdCommand.Transaction = bdTransaction
bdCommand.Connection = bdConection
bdCommand.CommandType = CommandType.Text
bdCommand.CommandTimeout = 80
Catch ex As Exception
sErro(ex.Message)
End Try
End Sub
Function sBDSelectSQL(ByVal comando As String, ByRef rs As Data.DataTable) As Integer
If rs Is Nothing Then : rs = New DataTable : Else : rs.Clear() : End If
Try
bdCommand.Parameters.Clear()
bdCommand.CommandText = PrepareStringToDB(comando)
Using dr As Data.SqlClient.SqlDataReader = bdCommand.ExecuteReader(CommandBehavior.Default)
rs.Load(dr)
End Using
Return rs.Rows.Count
Catch ex As Exception
sErro(PrepareStringToDB(comando) & " - " & ex.Message)
Return -1
End Try
End Function
Function sBDNonQuery(ByVal comando As String) As Boolean
Try
bdCommand.Parameters.Clear()
bdCommand.CommandText = PrepareStringToDB(comando)
bdCommand.ExecuteNonQuery()
Return True
Catch ex As Exception
sErro(PrepareStringToDB(comando) & " - " & ex.Message)
Return False
End Try
End Function
Function sBDReturnQuery(ByVal comando As String) As String
Try
bdCommand.Parameters.Clear()
bdCommand.CommandText = PrepareStringToDB(comando)
Return bdCommand.ExecuteScalar()
Catch ex As Exception
sErro(PrepareStringToDB(comando) & " - " & ex.Message)
Return "-1"
End Try
End Function
Sub sBDCloseConnect(ByVal erro As Boolean)
If erro Then
Try : bdTransaction.Rollback() : Catch : End Try
Try : bdConection.Close() : Catch : End Try
Else
Try : bdTransaction.Commit() : Catch : End Try
Try : bdConection.Close() : Catch : End Try
End If
End Sub
End Class
IDisposable
Add a class level boolean 'InDBTransaction'. Set it to true after starting the transaction. Set it to false after committing the transaction. Test in the dispose method and roll back if necessary. – Mort May 9 at 6:53