When executing multiple queries what is the best practice when using Commands and DataReaders? is it best to create only one and dispose/close it before using again or create a new one everytime and dispose/close that one? For example...
Dim sqlcmd as SqlCommand
Dim sqldr as SqlDatareader
sqlcmd = new SqlCommand(Query here, connection)
sqldr = sqlcmd.ExecuteReader
'Do stuff
sqlcmd.Dispose()
sqldr.Close()
sqlcmd = new SqlCommand(Different Query here, connection)
sqlcmd.ExecuteNonQuery
'Do stuff
sqlcmd.Dispose()
'And so on
Or
Dim sqlcmd as SqlCommand = new SqlCommand(Query here, connection)
Dim sqldr as SqlDataReader = sqlcmd.ExecuteReader
'Do stuff
sqlcmd.Dispose()
sqldr.Close()
Dim anothersqlcmd as SqlCommand = new SqlCommand(Different Query here, connection)
anothersqlcmd.ExecuteNonQuery
'Do stuff
anothersqlcmd.Dispose()
'And so on
Sorry about the example, i'm aware of the using statement but my example is the same. Is it better to be just using 1 Command and DataReader or creating a new one everytime?
I think regardless the allocation is there, for readability sake I would go with option two to identify when I have a new command with different parameters
In your example, it doesn't matter. They are both the same. But in your sample 2 you have a bug by calling sqlcmd.ExecuteNonQuery.
I personally would put them in two separate method and call the method instead. Just make sure the connection object is open only when needed and close is right away when not needed.
This is just what I do, not necessarily Best Practices but I am very careful to close my connections and dispose of my objects.
cn.Open()
If Mode = "Add" Then
cmd.CommandText = "InsertVendor"
RetVal = cmd.ExecuteNonQuery
Else 'Update
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = itgID
cmd.CommandText = "UpdateVendors"
RetVal = cmd.ExecuteNonQuery
End If
cn.Close()
cmd.Dispose()
Here, I used one command object but tailored it for 2 different operations. To get everything cleaned up I use Try..Catch..Finally
Finally
If Not IsNothing(cmd) Then
cmd.Dispose()
End If
If cn.State = ConnectionState.Open Then
cn.Close()
End If
End Try
Then check on the state of my objects there. There should never be an exception in a Finally block. I do use the same connection object throughout my DataAccess Class. Be careful of the DataReader; don't close your connection until it has finished its work.
Related
I have two connections types to be used in my app. One is SQL Server, using the SqlClient.Connection type. The other one uses the OleDb.Connection type.
The question is, when using a DataAdapter, DataReader, and Command, how can I use one reference for each with both connections?
As an example, I want to use a reader like this:
Dim MyReader As OleDb.OleDbDataReader
to check my data with an OleDbConnection, and then use same reader to check data from the second SqlClient connection. That is, I want to do something like this (pseudocode):
Dim con
Dim MyReader
con = oledb.connection
MyReader = mymethod(con)
con = sql.sqlclient.conection
MyReader = mymethod2(con)
How can I do this in real code?
I need help in how to declare data components for two different connection types inside the same method or function.
You should declare multiple variables. It's really not a big deal to do so.
But, if you really want to do this (again: not the best idea) one thing you can keep in mind is these objects all inherit from a common set of types in the System.Data.Common namespace. So it possible to write code like this:
Dim con As System.Data.Common.DbConnection = New OleDbConnection("connection string here")
Dim cmd As System.Data.Common.DbCommand = New OleDbCommand("SELECT * ... ", con)
con.Open()
Dim rdr As System.Data.Common.DbDataReader = con.ExecuteReader()
While rdr.Read()
' ...
End While
con.Close()
con = New SqlClient.SqlConnection("connection string here")
cmd = New SqlClient.SqlCommand("SELECT * ...", con)
con.Open()
rdr = cmd.ExecuteReader()
While rdr.Read()
' ...
End While
But again: you really are better off using separate variables.
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
Have already looked questions similar to mine but none of them works for me this is my code
dbconn = New SqlConnection
dbconn.ConnectionString = ("Data Source=JENELIE\SQLEXPRESS;Initial Catalog=feeding_monitoring_system;User ID=sa;Password=Jenelie19; MultipleActiveResultSets = true")
Dim reader As SqlDataReader
Dim sda As New SqlDataAdapter
Dim ds As New DataSet()
Try
dbconn.Open()
Dim sql As String
sql = "select Count (Gender) as NumberofStudent, Gender from Student_Info Group by Gender"
dbcomm = New SqlCommand(sql, dbconn)
reader = dbcomm.ExecuteReader
sda.SelectCommand = dbcomm
sda.Fill(ds, "Student_Info")
Catch ex As SqlException
MessageBox.Show(ex.Message)
Finally
dbconn.Dispose()
End Try
Then at sda.Fill(ds, "Student_Info") an error happens
You dont use that reader at all, so i don't understand your code. You want to fill the DataSet with the DataAdapter, then this is needed (always use Using):
Dim ds As New DataSet()
Using dbconn As New SqlConnection("Data Source=JENELIE\SQLEXPRESS;Initial Catalog=feeding_monitoring_system;User ID=sa;Password=Jenelie19;MultipleActiveResultSets = true")
Dim sda = New SqlDataAdapter("select Count (Gender) as NumberofStudent, Gender from Student_Info Group by Gender", dbconn)
Try
sda.Fill(ds, "Student_Info") ' you dont need to open/close the connection
Catch ex As SqlException
MessageBox.Show(ex.Message)
End Try
End Using
I would try making sure that all disposable objects are properly disposed within this function. I recommend the Using statement to help ensure that any disposable object gets properly disposed as it goes out of scope. I believe that SqlConnection, SqlDataReader, SqlDataAdapter and DataSet are all disposable.
Edit: Although I think Tim's answer is more targeted at your problem (the SqlDataReader is unused and unnecessary), you should make sure to clean-up all your disposable objects, too. If you do use an SqlDataReader, you'll want to dispose of it before doing anything else, unless you're just trying to prove that you can have multiple result sets open at once, in which case, the lack of cleanup in multiple accesses to the same connection might be responsible (if one of them doesn't include MultipleActiveResultSets).
First off, in that situation, you do not need to use reader, you just need SQLDataAdapter.
Second, you should use Conn.Close() to close your SQL connection, rather than Conn.Dispose(). The error means that some where in your code, you opened the connection before hand, but never closed it.
I develop a lot in ASP.NET and I know that you can only open one SQLDataReader for each SQLConnection. However, this does not appear to be the case in VB.NET (form application) i.e. I have opened multiple SQLDataReaders for one connection object. Is this allowed in VB.NET?
If there is not an obvious answer to this then I will post some code.
Here is some code:
Public Function CheckActiveReviews()
Dim objCon As SqlConnection
Dim objCommand As SqlCommand, objCommand2 As SqlCommand
Dim objDR As SqlDataReader, objDR2 As SqlDataReader
Try
objCon = New SqlConnection("Data Source=TestDatabase;Initial Catalog=TestTable;User ID=TestUser;Password=TestPassword;MultipleActiveResultSets=True")
objCommand = New SqlCommand
objCommand.Connection = objCon
objCommand2 = New SqlCommand
objCommand2.Connection = objCon
objCon.Open()
objCommand.CommandText = "SELECT ID FROM Person WHERE PersonID > 1000"
objDR = objCommand.ExecuteReader()
Do While objDR.Read
objCommand2.CommandText = "SELECT * FROM Sport WHERE PersonID = #PersonID "
objCommand2.Parameters.AddWithValue("#PersonID", objDR("ID"))
objDR2 = objCommand2.ExecuteReader
Loop
Catch ex As Exception
End Try
End Function
You can use multiple data readers if you use MARS - Multiple Active Result Sets - but I wouldn't advise that unless you really need it.
Instead, I'd suggest creating a new SqlConnection object each time you need it - use it for as short a period as you can, then dispose of it (use a Using statement to do this for you). The connection pool will take care of the efficiency in terms of reusing "physical" network connections where possible. That way you don't need to worry about whether the SqlConnection is already open etc - you just always follow the same "create, open, use, dispose" pattern every time.
Should I use using?
Private Sub btntest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btntest.Click
If sqlConnection.State = ConnectionState.Closed Then
sqlConnection.Open()
End If
Dim query = "Select * from tablebusiness"
Dim cmd = New MySqlCommand(query, sqlConnection)
Dim data = cmd.ExecuteReader()
Do While data.Read
Loop
Dim cmd1 = New MySqlCommand(query, sqlConnection)
Dim data1 = cmd1.ExecuteReader //Error. Already have data reader
//Error There is already an open DataReader associated with this Connection which must be closed first.
Dim check = 1
'sqlConnection.Close()
End Sub
Although you have not let us know what the error is (which makes solving any problem much harder), I expect the issue is arising because you are trying to re-use the SqlConnection object for 2 different commands. Especially since you are not disposing your first command before initialising the second.
Firstly, use 2 different SqlConnection objects to manage the connection to the database. You are not putting any more overhead on the database or the code if you do this. Let the .NET framework connection pooling do its job - don't try to do it yourself. You don't need to do anything specific to enable connection pooling (although you can disable it by setting Pooling=false in your connection string).
Secondly use the using statement to correctly dispose your SqlConnection, SqlCommand, and SqlDataReader objects. e.g.
Using connection As New SqlConnection(connectionString)
connection.Open()
Using Command As New SqlCommand(query, connection)
Using reader As SqlDataReader = Command.ExecuteReader()
While reader.Read()
'Do Stuff'
End While
End Using
End Using
connection.Close()
End Using
You have missed the parenthesis after cmd1.ExecuteReader. It should be cmd1.ExecuteReader().
You Need another Conncetion if you want both the datareaders to work simultaneously, else close/ dispose the previous command before using cmd1.ExecuteReader()