How do I track down an Unclosed reader - sql

I've got a utility function in a much larger project that updates a backend SQL database. It's currently failing most times I use it, with the error:
There is already an open DataReader associated with this Command which must be closed first.
The code for the function is below:
Public Function Update_Data(what As String, Optional where As String = "",
Optional table As String = ThisAddIn.defaultTable) As Integer
Try
Dim cmd As New SqlCommand With {
.Connection = conn
}
cmd.CommandText = "UPDATE " & table & " SET " & what
If where <> "" Then
cmd.CommandText &= " WHERE " & where
End If
Update_Data = cmd.ExecuteNonQuery
cmd.Dispose()
Catch ex As Exception
Update_Data = 0
Debug.WriteLine("SQL Error updating data:" & vbCrLf & ex.Message)
End Try
End Function
I've gone through the rest of the code to make sure that whenever I have a SQLDataReader declared I later call reader.close(). I added the cmd.Dispose() line to this and all the other ExecuteNonQuery functions I could find - incase that helped?
Are there any other instances/types of reader that might not be being closed?

In the case of an Exception, you aren't disposing your command.
If you don't want to use Using, add a Finally
Public Function Update_Data(what As String, Optional where As String = "",
Optional table As String = ThisAddIn.defaultTable) As Integer
Dim cmd As SqlCommand
Try
cmd = New SqlCommand With {.Connection = conn}
cmd.CommandText = "UPDATE " & table & " SET " & what
If where <> "" Then
cmd.CommandText &= " WHERE " & where
End If
Update_Data = cmd.ExecuteNonQuery
Catch ex As Exception
Update_Data = 0
Debug.WriteLine("SQL Error updating data:" & vbCrLf & ex.Message)
Finally
cmd.Dispose()
End Try
End Function
but Using might be simpler
Public Function Update_Data(what As String, Optional where As String = "",
Optional table As String = ThisAddIn.defaultTable) As Integer
Using cmd As New SqlCommand With {.Connection = conn}
Try
cmd.CommandText = "UPDATE " & table & " SET " & what
If where <> "" Then
cmd.CommandText &= " WHERE " & where
End If
Update_Data = cmd.ExecuteNonQuery
Catch ex As Exception
Update_Data = 0
Debug.WriteLine("SQL Error updating data:" & vbCrLf & ex.Message)
End Try
End Using
End Function

Related

How solve this problem syntax error in UPDATE statement

This problem at syntax error for update statement then I don't know how to solve this problem
Private Sub editStaff()
Try
If con.State = ConnectionState.Closed Then
con.Open()
End If
If IDTextBox.Text <> "" And FirstTextBox.Text <> "" And SecondTextBox.Text <> "" And UsernameTextBox.Text <> "" And PasswordTextBox.Text <> "" Then
strSQL = "update Staff set First_Name = '" & FirstTextBox.Text & "', " &
"Second_Name = '" & SecondTextBox.Text & "', " & "Username = '" & UsernameTextBox.Text & "', " &
"Password = '" & PasswordTextBox.Text & "'" & " where ID = " & CInt(IDTextBox.Text) & ""
Dim cmd As OleDbCommand = New OleDbCommand(strSQL, con)
Try
cmd.ExecuteNonQuery()
cmd.Dispose()
con.Close()
MessageBox.Show("Update Successful")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
For some reason your validation If did not include the ID text box. I added validation for this text box. The OrElse is a short circuit. As soon as it finds a True it stops checking the conditions and proceeds to the next line.
This code
If con.State = ConnectionState.Closed Then
con.Open()
End If
is completely unnecessary if you keep your database objects local. Keeping them local allows you to ensure they are closed and disposed with Using...End Using blocks.
Don't open the connection until you need it which is directly before the .Execute... line. Use parameters to avoid Sql Injection. Also your Update statement is much easier to write without all the single quotes and double quotes and ampersands.
Caution In Access the order that the parameters appear in the Sql statement must match the order that they are added to the .Parameters collection.
Finally, you should NEVER store passwords as plain text. I will leave it to you to research salting and hashing and correct the code.
Private Sub editStaff()
Dim i As Integer
If Integer.TryParse(IDTextBox.Text, i) Then
MessageBox.Show("ID text box must be a number")
Return
End If
If IDTextBox.Text = "" OrElse FirstTextBox.Text = "" OrElse SecondTextBox.Text = "" OrElse UsernameTextBox.Text = "" OrElse PasswordTextBox.Text = "" Then
MessageBox.Show("Please fill in all text boxes")
Return
End If
Try
Using con As New OleDbConnection("Your connection string")
Dim strSQL = "Update Staff set First_Name = #FirstName, Second_Name = #SecondName, [Username] = #UserName, [Password] = #Password Where [ID] = #ID"
Using cmd As New OleDbCommand(strSQL, con)
With cmd.Parameters
.Add("#FirstName", OleDbType.VarChar).Value = FirstTextBox.Text
.Add("#SecondName", OleDbType.VarChar).Value = SecondTextBox.Text
.Add("#UserName", OleDbType.VarChar).Value = UsernameBox.Text
.Add("#Password", OleDbType.VarChar).Value = PasswordTextBox.Text
.Add("#ID", OleDbType.Integer).Value = CInt(IDTextBox.Text)
End With
con.Open()
cmd.ExecuteNonQuery()
End Using
End Using
MessageBox.Show("Update Successful")
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub

Insert into database in vb not working

I have the below snippet of code whenever I try registering the username exist part seems to be executed even for a username that doesn't exist in the database. Don't know where am wrong here any help will be appreciated.
'Connecting to SQL Database and executing Query------------------------------------------
Dim Strconn As String = "Data Source=.\SQLEXPRESS; Database=QuizDB; Integrated Security = true"
Dim Strcmd As String = "INSERT INTO reg_info(uname,pass,fname,lname,dob,course,college) VALUES ('" & user_name.Text & "','" & con_pass.Text & "', '" & first_name.Text & "', '" & last_name.Text & "', '" & dob.Text & "', '" & course.Text & "', '" & college.Text & "');"
Dim da As New SqlDataAdapter
Dim ds As New DataSet
Dim sqlcmd As SqlCommand
sqlconn = New SqlConnection(Strconn)
Try
sqlconn.Open()
Catch ex As Exception
MsgBox("Could not connect to DataBase. Application will close now!", vbCritical, "Database Error")
End
End Try
sqlcmd = New SqlCommand(Strcmd, sqlconn)
da.SelectCommand = sqlcmd
sqlcmd.Dispose()
sqlconn.Close()
'Exception Handling-----------------------
Dim exc As Exception = Nothing
Try
da.Fill(ds)
Catch ex As Exception
exc = ex
Finally
If Not (exc) Is Nothing Then
MsgBox("User Name Already Exist. Please select a different User Name!", vbExclamation, "Already Exist")
user_name.Focus()
Else
MsgBox("Registration Successful.", vbInformation, "Successful")
Me.Close()
Login.Show()
End If
End Try
Here is a refactor of your code with some helpful guidance. I think this will compile, but if it does not, then you can do a little homework to figure out what is missing.
Try
' the USING block guarantees that the object's Close() and Dispose() methods are fired automatically when you exit the block
Using sqlconn As New SqlConnection("Data Source=.\SQLEXPRESS; Database=QuizDB; Integrated Security = true")
Using sqlcmd As SqlCommand = sqlconn.CreateCommand
With sqlcmd
.CommandType = CommandType.Text
' parameterized query to protect against SQL injection
.CommandText = "INSERT INTO reg_info(uname,pass,fname,lname,dob,course,college) VALUES (#username, #password, #firstname, #lastname, #dob, #course, #college)"
With .Parameters
.Clear()
.AddWithValue("#username", user_name.Text)
.AddWithValue("#password", con_pass.Text)
.AddWithValue("#firstname", first_name.Text)
.AddWithValue("#lastname", last_name.Text)
.AddWithValue("#dob", dob.Text)
.AddWithValue("#course", course.Text)
.AddWithValue("#college", college.Text)
End With
.ExecuteScalar() ' Actually executes the SQL command
End With
End Using
End Using
MsgBox("Registration successful")
Catch ex As Exception
' Any error in the TRY block will automatically jump to herem and the "ex" object will be an Exception object with populated properties
MsgBox("User name already exists. Error from database is " & ex.Message)
End Try

Execute two INSERT statements

I have two seperate INSERT statements that I want to run one after the other,
the first one runs successfully but it seems like it isn't even getting to the second statement any idea why?
Try
con.Open()
cmd.ExecuteNonQuery()
resultSQL.ForeColor = Drawing.Color.Green
resultSQL.Text = "Successfully Saved"
Catch ex As Exception
Response.Write(ex.Message)
resultSQL.ForeColor = Drawing.Color.Red
resultSQL.Text = ex.Message
Finally
End Try
INSERT 2 which is not running:
strQuery = "INSERT INTO [SD_EPOS_Entry]" _
& " ([Trade_Activity_Code] " _
& " VALUES " _
& " (#Trade_Activity_Code1)"
cmd = New SqlCommand(strQuery)
cmd.Parameters.AddWithValue("#Trade_Activity_Code1", tcodeVar)
Try
con.Open()
cmd.ExecuteNonQuery()
resultSQL.ForeColor = Drawing.Color.Green
resultSQL.Text = "Successfully Saved2"
Catch ex As Exception
Response.Write(ex.Message)
resultSQL.ForeColor = Drawing.Color.Red
resultSQL.Text = ex.Message
Finally
Right off the bat there seems to be a missing parenthesis in your second query:
strQuery = "INSERT INTO [SD_EPOS_Entry]" _
& " ([Trade_Activity_Code] " _
& " VALUES " _
& " (#Trade_Activity_Code1)"
It should be:
strQuery = "INSERT INTO [SD_EPOS_Entry]" _
& " ([Trade_Activity_Code]) " _
& " VALUES " _
& " (#Trade_Activity_Code1)"
I would also advise for proper disposal of any SqlCommand and SqlConnection instances you create, either by using the Dispose() method or declaring them in a Using statement.
If as you say your second query isn't even reached, follow your code at runtime with a debugger and look at which point it stops. That ougth to provide some clue.
UPDATE:
This should execute both your INSERT statements in a single operation:
strQuery = "INSERT INTO [SD_T_Code]" _
& " ([Trade_Activity_Code]) " _
& " VALUES " _
& " (#Trade_Activity_Code)" _
& ";INSERT INTO [SD_EPOS_Entry]" _
& " ([Trade_Activity_Code]) " _
& " VALUES " _
& " (#Trade_Activity_Code)"
Dim strMessage As String = "Successfully saved"
Dim resultColor As Drawing.Color = Drawing.Color.Green
Using cmd = New SqlCommand(strQuery, con)
cmd.Parameters.AddWithValue("#Trade_Activity_Code", tcodeVar)
Try
con.Open()
cmd.ExecuteNonQuery()
resultSQL.ForeColor = Drawing.Color.Green
resultSQL.Text = "Successfully Saved"
Catch ex As SqlException
strMessage = ex.Message
resultColor = Drawing.Color.Red
Finally
con.Close()
End Try
End Using
// Write results outside of db operation
Response.Write(strMessage)
resultSQL.ForeColor = resultColor
resultSQL.Text = strMessage
Of course the above assumes there's an instanciated, unopen con connection variable present.
Notice that it also only catches SqlException. Catching the basic Exception here seems wrong to me.
SECOND UPDATE:
If the above code isn't working, the issue may be caused by an Sql Trigger.
Somewhere not shown in your code, you are assigning the connection to the command object (this is for the first query).
When you get to the second query, you're redefining the cmd object as a new object, which is also removing any reference to the connection as established earlier.
Try this for your second block of code:
strQuery = "INSERT INTO [SD_EPOS_Entry]" _
& " ([Trade_Activity_Code] " _
& " VALUES " _
& " (#Trade_Activity_Code1)"
cmd = New SqlCommand(strQuery, cnn) 'Define the connection to be used for the command.

Can't figure out what's wrong when trying to update a database entry

I can't figure out why updating my database entries wont work. Sometimes it tells me there is a syntax error, and other times when I try to delete an entry after trying an update it tells me that the connection was not closed. I'm not that familiar with SQL so any help would be appreciated.
Public Shared Property filename As String
Private dbConnection As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=IT_Checkout.accdb")
Private dbAdapter As OleDbDataAdapter
Private dbDataset As DataSet
Public Function deleteReport(ByVal rID As String,
ByRef msg As String) As Boolean
Dim sql As String
sql = "DELETE FROM Reports WHERE RID = '" & rID & "'"
If do_command(sql, msg) = False Then
Return False
End If
If File.Exists(My.Application.Info.DirectoryPath & "\reports\" & rID & ".dat") Then
My.Computer.FileSystem.DeleteFile(My.Application.Info.DirectoryPath & "\reports\" & rID & ".dat")
End If
Return True
End Function
Public Function updateReport(ByVal r As Report,
ByRef msg As String) As Boolean
Dim sql As String = "UPDATE Reports SET Name='" & r.name & "', Date='" & r.outDate & "', Notes='" & r.notes & "', Archived='" & r.archived.ToString & "' WHERE RID='" & r.getID & "'"
Return do_command(sql, msg)
End Function
Public Function do_command(ByVal sql As String,
ByRef msg As String) As Boolean
Dim command As OleDbCommand
Try
dbConnection.Open()
command = New OleDbCommand(sql, dbConnection)
command.ExecuteNonQuery()
dbConnection.Close()
Return True
Catch ex As Exception
msg = "From Command: " & ex.Message
Return False
End Try
End Function
If any of your values contain apostrophes your syntax will be wrong - you should use parameters instead of concatenating SQL:
Dim sql As String = "UPDATE Reports SET [Name]=?, [Date]=?, Notes=?, Archived=? WHERE RID=?"
command = New OleDbCommand(sql, dbConnection)
command.Parameters.Add("Name").Value = r.name
command.Parameters.Add("Date").Value = r.outDate
command.Parameters.Add("Notes").Value = r.notes
command.Parameters.Add("Archived").Value = r.archived
command.Parameters.Add("RID").Value = r.getID
command.ExecuteNonQuery()
dbConnection.Close()
You should also not share a single connection object - connections are pooled by .NET and are cheap to create.

ODBC - unable to allocate an environment handle

Hi I am trying to insert data into Navision database from a DataTable. That DataTable contain around 5000 records, if the records count is less then it's working fine but record count is around 5000 I am getting this error.
ERROR - unable to allocate an environment handle.
This is the code I am using
Public Function InsertToHHTTransferLine(ByVal dtTransferLn As DataTable, ByVal hhtNumber As String) As Integer
Dim result As Integer
Dim cn As OdbcConnection
Dim dtTransferLine As DataTable
cn = New OdbcConnection(ConnStr)
Dim SqlStr As String = ""
Try
cn.Open()
dtTransferLine = dtTransferLn
Dim DocType As String
DocType = "Purchase"
Dim cmd As OdbcCommand
Dim hhtNo As String
hhtNo = hhtNumber
If dtTransferLine.Rows.Count > 0 Then
For i As Integer = 0 To dtTransferLine.Rows.Count - 1
If dtTransferLine.Rows(i)("DOC_TYPE").ToString() = "0" Then
DocType = "Purchase"
End If
If dtTransferLine.Rows(i)("DOC_TYPE").ToString() = "1" Then
DocType = "Transfer Receipt"
End If
If dtTransferLine.Rows(i)("DOC_TYPE").ToString() = "2" Then
DocType = "Transfer Shipment"
End If
If dtTransferLine.Rows(i)("DOC_TYPE").ToString() = "3" Then
DocType = "Stock Count"
End If
Try
SqlStr = "INSERT INTO ""HHT & Navision Line""(""Document Type"",""Document No_"",""HHT No_"",""Line No_"",""Item No_"",""Document Quantity"",""Scan Quantity"",""Unit Price"",""Posted"") VALUES('" & DocType & "','" & dtTransferLine.Rows(i)("DOC_NO").ToString() & "','" & hhtNo & "','" & dtTransferLine.Rows(i)("LINE_NO").ToString() & "','" & dtTransferLine.Rows(i)("ITEM_NO").ToString() & "'," & dtTransferLine.Rows(i)("DOC_QTY").ToString() & "," & dtTransferLine.Rows(i)("SCAN_QTY").ToString() & "," & dtTransferLine.Rows(i)("UNIT_PRICE").ToString() & ",0)"
cmd = New OdbcCommand(SqlStr, cn)
result = cmd.ExecuteNonQuery()
Catch ex As Exception
If (ex.Message.IndexOf("Illegal duplicate key") <> -1) Then
CreateLog(SqlStr, "User1", "Duplicate()", ex.Message)
Else
CreateLog(SqlStr, "User1", "Other()", ex.Message)
End If
'CreateLog(SqlStr, "User1", "Other()", ex.Message)
End Try
Next
End If
Catch ex As Exception
CreateLog(SqlStr, "User1", "InsertToHHTTransferLine()", ex.Message)
result = -1
Finally
cn.Close()
cn.Dispose()
End Try
Return result
End Function
Could be that "cmd" variables need to be disposed before creating new ones?
At least this is the only thing I can see in the code where you are consuming resources in a loop depending on the number of records.
Anyway this should be easy to identify with a debugger, just figure out the line giving you the error, and that will lead you to the answer.