VB.NET - Multiple SQLDataReader's - vb.net

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.

Related

Two Connections types in one method (SQL and OLEDB)

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.

Data reader already open

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.

Best practice when executing multiple queries

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.

TransactionScope error

I am seeing the following error when I attempt to open the second connection in the code below: Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.
Public Function Test()
Using Scope = New TransactionScope
getMailServer()
getMailServer()
End Using
End Function
Private Function getMailServer() As String
Dim objCommand As SqlCommand, objCommand2 As SqlCommand
Dim objCon As SqlConnection
Dim intDeleteCount As Integer
Dim objDR As SqlDataReader
Dim strServer As String
Try
objCommand = New SqlCommand
objCommand2 = New SqlCommand
objCon = New SqlConnection(_ConString) 'taken from web.config
objCon.Open()
objCommand.Connection = objCon
Using objCon
Using objCommand
objCommand2.Connection = objCon
objCommand2.CommandText = "SELECT SMTPServer FROM dbServer"
objDR = objCommand2.ExecuteReader
Do While objDR.Read
strServer = objDR("SMTPServer")
Loop
objDR.Close()
End Using
End Using
Return strServer
Catch ex As Exception
Throw
Finally
End Try
End Function
Please note that I have spent some time Googling this and I have tried a few things posted on this website e.g. restarting the districuted transaction coordinator in Services. I did also read somewhere that using TransactionScope should be avoided with distributed transactions (those with multiple connection objects). I am not sure if this is true.
You can use TransactionScope for distributed transactions. In fact, by introducing TransactionScope in .NET 2.0, Microsoft put COM+ out of its misery. It was a very good move, COM+ was awful.
You need to configure DTC for network access on all machines participating in the transaction - the one running your code, as well as those running the databases (or other resources you may be using, such as MSMQ).
Here's how to enable DTC network access.

Closing SqlConnection without Closing SQL reader - can it cause connection Leak

I have been using SQLDataReader for fetchig some data from a db. Once I used Reader with the the connection I am closing the connection only and not the Reader. Do we have any possibility of connection leak
Here is the code that I am using
Public Sub Get_SomeData(ByVal sCon As String,ByRef ObjectToReturn As SomeClass)
Dim sqlCon As SqlConnection = New SqlConnection(sCon)
Dim sqlR As SqlDataReader = Nothing
Dim sqlCmd As SqlCommand = New SqlCommand
sqlCmd.CommandType = CommandType.StoredProcedure
sqlCmd.Connection = sqlCon
sqlCmd.CommandText = "get_SomeData"
sqlCon.Open()
sqlR = sqlCmd.ExecuteReader(CommandBehavior.CloseConnection)
If sqlR.HasRows And sqlR.Read Then
ObjectToReturn.Property1 = sqlR("Column1").ToString
ObjectToReturn.Property1 = sqlR("Column1").ToString
ObjectToReturn.Property1 = sqlR("Column1").ToString
ObjectToReturn.Property1 = sqlR("Column1").ToString
End If
sqlCon.Close()
End Sub
No, closing the connection will suffice, but a better approach is through the Using statement
Using sqlCon = New SqlConnection(sCon)
Dim sqlR As SqlDataReader = Nothing
Using sqlCmd = New SqlCommand
sqlCmd.CommandType = CommandType.StoredProcedure
sqlCmd.Connection = sqlCon
sqlCmd.CommandText = "get_SomeData"
sqlCon.Open()
Using sqlR = sqlCmd.ExecuteReader()
If sqlR.HasRows And sqlR.Read Then
ObjectToReturn.Property1 = sqlR("Column1").ToString
.......
End If
End Using
End Using
End Using
The important part in MSDN docs about Using
Sometimes your code requires an unmanaged resource, such as a file
handle, a COM wrapper, or a SQL connection. A Using block guarantees
the disposal of one or more such resources when your code is finished
with them. This makes them available for other code to use.
Managed resources are disposed of by the .NET Framework garbage
collector (GC) without any extra coding on your part. You do not need
a Using block for managed resources. However, you can still use a
Using block to force the disposal of a managed resource instead of
waiting for the garbage collector.