How do I solve a connection pool issue? - sql-server

I have recently started encountering Database connection issues with SQL Server on my development machine.
System.InvalidOperationException: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool
How can I monitor the connection pool to figure out what is happening?
Further Info:
I haven't had much luck with this - I'm definitely not leaking connections. Every connection is inside a using statement.
When the problem does occur, I have the Performance Monitor window open and it's not showing anywhere near the limit of the pool (which is 100) - generally around 2 - 5 connections, so I don't think the pool is being exhausted so maybe it's a timeout.
However, I have set the ConnectionTimeout to 0 - which, according to the documentation, means it should wait forever to connect - but I'm not seeing this.
When it does occur, it happens fairly quickly - I'm running under the debugger from VS2010 - starting a new instance of my application - and it might happen within a second or two of starting - in starting up the app there are several queries that happen. The actual SQL Server I'm running against is SQL Express 2008. Maybe I should try running it against SQL Server 2008 and see if I see any different behaviour.
Any other ideas?

Take a look at the ADO.NET Performance Counters related to pooling.
Your described symptom is often an indication that you are leaking connections. Make sure all connections are disposed when you are finished with them, preferably by wrapping in an using statement.

here's some code to try the pool and then failover to unpooled:
use this sub if a problem happens with the pool:
Public Sub OpenConn()
Dim sTempCNString As String = cn.ConnectionString
Try
' add a timeout to the cn string, following http://www.15seconds.com/issue/040830.htm
Dim iTimeOut As Integer = utils_Configuration.Get_ConfigInt("DBConnectTimeout", 0)
If (iTimeOut > 0 And Not cn.ConnectionString.ToLower.Contains("timeout")) Then
Diagnostics.Debug.Print("<><><><><><><> SHORT CONNECT WITH POOLING <><><><><><><><><> ")
cn.ConnectionString += ";Connect Timeout=" & iTimeOut.ToString() & ";"
End If
cn.Open()
IsOperational = True
Catch ex As Exception
Diagnostics.Debug.Print("ERROR IN OPENING, try no pool")
' see http://www.15seconds.com/issue/040830.htm
' turn off pooling
Diagnostics.Debug.Print("<><><><><><><> CONNECT WITHOUT POOLING <><><><><><><><><> ")
Dim sAddOn As String = ";Pooling=false;Connect Timeout=45;"
cn.ConnectionString = sTempCNString & sAddOn
cn.ConnectionString = cn.ConnectionString.Replace(";;", ";")
cn.Open()
End Try
End Sub
Here's some code to monitor the pool:
Option Explicit On
Option Strict On
Imports System.Data.SqlClient
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
' ref: http://msdn2.microsoft.com/en-us/library/ms254503.aspx
Public Class utils_SqlPerfMon
Private PerfCounters(9) As PerformanceCounter
Private connection As SqlConnection
Public sConnectString As String = ""
Public sResult As String = ""
Public Sub New()
sConnectString = Tools.GetMainDBConn().ConnectionString
connection = New SqlConnection(sConnectString)
Exec()
End Sub
Public Sub New(ByVal strC As String)
sConnectString = strC
connection = New SqlConnection(sConnectString)
Exec()
End Sub
Public Sub Exec()
Me.SetUpPerformanceCounters()
Diagnostics.Debug.Print("Available Performance Counters:")
' Create the connections and display the results.
Me.CreateConnectionsAndDisplayResults()
End Sub
Private Sub CreateConnectionsAndDisplayResults()
' List the Performance counters.
WritePerformanceCounters()
Dim connection1 As SqlConnection = New SqlConnection( _
Me.sConnectString)
connection1.Open()
Diagnostics.Debug.Print("Opened the 1st Connection:")
WritePerformanceCounters()
connection1.Close()
Diagnostics.Debug.Print("Closed the 1st Connection:")
WritePerformanceCounters()
Return
End Sub
Private Enum ADO_Net_Performance_Counters
NumberOfActiveConnectionPools
NumberOfReclaimedConnections
HardConnectsPerSecond
HardDisconnectsPerSecond
NumberOfActiveConnectionPoolGroups
NumberOfInactiveConnectionPoolGroups
NumberOfInactiveConnectionPools
NumberOfNonPooledConnections
NumberOfPooledConnections
NumberOfStasisConnections
' The following performance counters are more expensive to track.
' Enable ConnectionPoolPerformanceCounterDetail in your config file.
' SoftConnectsPerSecond
' SoftDisconnectsPerSecond
' NumberOfActiveConnections
' NumberOfFreeConnections
End Enum
Private Sub SetUpPerformanceCounters()
connection.Close()
Me.PerfCounters(9) = New PerformanceCounter()
Dim instanceName As String = GetInstanceName()
Dim apc As Type = GetType(ADO_Net_Performance_Counters)
Dim i As Integer = 0
Dim s As String = ""
For Each s In [Enum].GetNames(apc)
Me.PerfCounters(i) = New PerformanceCounter()
Me.PerfCounters(i).CategoryName = ".NET Data Provider for SqlServer"
Me.PerfCounters(i).CounterName = s
Me.PerfCounters(i).InstanceName = instanceName
i = (i + 1)
Next
End Sub
Private Declare Function GetCurrentProcessId Lib "kernel32.dll" () As Integer
Private Function GetInstanceName() As String
'This works for Winforms apps.
'Dim instanceName As String = _
' System.Reflection.Assembly.GetEntryAssembly.GetName.Name
' Must replace special characters like (, ), #, /, \\
Dim instanceName As String = _
AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
.Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")
'For ASP.NET applications your instanceName will be your CurrentDomain's
'FriendlyName. Replace the line above that sets the instanceName with this:
'instanceName = AppDomain.CurrentDomain.FriendlyName.ToString.Replace("(", "[") _
' .Replace(")", "]").Replace("#", "_").Replace("/", "_").Replace("\\", "_")
Dim pid As String = GetCurrentProcessId.ToString
instanceName = (instanceName + ("[" & (pid & "]")))
Diagnostics.Debug.Print("Instance Name: {0}", instanceName)
Diagnostics.Debug.Print("---------------------------")
Return instanceName
End Function
Private Sub WritePerformanceCounters()
Dim sdelim As String = vbCrLf ' "<br>"
Diagnostics.Debug.Print("---------------------------")
sResult += "---------------------------"
sResult += sdelim
Dim strTemp As String = ""
For Each p As PerformanceCounter In Me.PerfCounters
Try
Diagnostics.Debug.Print("{0} = {1}", p.CounterName, p.NextValue)
strTemp = p.CounterName & "=" & p.NextValue.ToString
Catch ex As Exception
strTemp = ""
End Try
sResult += strTemp
sResult += sdelim
Next
Diagnostics.Debug.Print("---------------------------")
sResult += "---------------------------"
sResult += sdelim
End Sub
Private Shared Function GetSqlConnectionStringDifferent() As String
' To avoid storing the connection string in your code,
' you can retrive it from a configuration file.
Return ("Initial Catalog=AdventureWorks;Data Source=.\SqlExpress;" & _
"User Id=LowPriv;Password=Data!05;")
End Function
End Class

Related

Query Timeout Expired when updating SQL Server from VB,NET

I'm updating SQL Server from VB.NET and keep getting the 'Query Timeout Error', I have lot's of sub routines that I run in sequence that look like the following:
Public Shared Sub Update_DailyRatings()
Dim stallStats As String = ""
Dim win As Integer = 0
Dim mSplit As Array
Dim cn As OleDbConnection = New OleDbConnection(MainForm.connectStringPublic)
cn.Open()
Dim selectString As String = "Select * FROM DailyRatings"
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim reader As OleDbDataReader = cmd.ExecuteReader()
While (reader.Read())
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
End While
reader.Close()
cn.Close()
End Sub
The NonQuery sub looks like this:
Public Sub NonQuery(ByVal SQL As String)
Dim query As String = SQL
Try
Dim cn3 As OleDbConnection = New OleDbConnection(connectStringPublic)
cn3.Open()
Dim cmd As OleDbCommand = New OleDbCommand(query, cn3)
cmd.CommandTimeout = 90
cmd.ExecuteNonQuery()
cn3.Close()
cn3.Dispose()
cmd.Dispose()
OleDbConnection.ReleaseObjectPool()
Catch e As System.Exception
Clipboard.SetText(query)
MsgBox(e.Message)
Finally
End Try
End Sub
As you can see I've been trying ideas to fix this that I found in other threads such as extending the timeout and using the Dispose() and ReleaseObjectPool() methods but it hasn't worked, I still get query timeout error at least once when running all my subs in sequence, it's not always the same sub either.
I recently migrated from Access, this never used to happen with Access.
If you are dealing with Sql Server why are you using OleDb? I guessed that is was really access.
While your DataReader is open, your connection remains open. With the amount of processing you have going on, it is no wonder that your connection is timing out.
To begin, connections and several other database objects need to be not only closed but disposed. They may contain unmanaged resources which are released in the .Dispose method. If you are using an object that exposes a .Dispose method use Using...End Using blocks. This will take care of this problem even if there is an error.
Actually you have 2 distinct operations going on. First you are retrieving DailyRatings and then you are updating DailyRatings base on the data retrieved. So we fill a Datatable with the first chunk of data and pass it off to the second operation. Our first connection is closed and disposed.
In operation 2 we create our connection and command objects just as before except now our command has parameters. The pattern of the command is identical for every .Execute, only the values of the parameters change. This pattern allows the database, at least in Sql Sever, to cache a plan for the query and improve performance.
Public Shared Function GetDailyRatings() As DataTable
Dim dt As New DataTable
Using cn As New OleDbConnection(MainForm.connectStringPublic),
cmd As New OleDbCommand("Select * FROM DailyRatings", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
Return dt
End Function
Public Sub UpdateDailyRatings()
Dim dt = GetDailyRatings()
Using cn As New OleDbConnection(connectStringPublic),
cmd As New OleDbCommand("UPDATE DailyRatings SET StallWin = #Stall WHERE Horse = #Horse")
cmd.Parameters.Add("#Stall", OleDbType.Integer)
cmd.Parameters.Add("#Horse", OleDbType.VarChar)
cn.Open()
For Each row As DataRow In dt.Rows
cmd.Parameters("#Horse").Value = row("Horse").ToString
Dim stallStats As String = Get_Stall_Stats(row("Track").ToString, CInt(row("Stall")), CDbl(row("Distance")))
If stallStats = "" Then
cmd.Parameters("#Stall").Value = 999
Else
cmd.Parameters("#Stall").Value = CInt(stallStats.Split(","c)(0))
End If
cmd.ExecuteNonQuery()
Next
End Using
End Sub
Private Function GetStallStats(Track As String, Stall As Integer, Distance As Double) As String
Dim s As String
'Your code here
Return s
End Function
Note: OleDb does not pay attention to parameters names. It is the order that they appear in the query statement must match the order that they are added to the Parameters collection.
It's possible that OleDbDataReader is locking your table or connection as it get the data with busy connection. You can store the data in a DataTable by using OleDbDataAdapter and loop through it to run your updates. Below is the snippet how your code would look like:
Dim cmd As OleDbCommand = New OleDbCommand(selectString, cn)
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter(cmd)
Dim dt As New DataTable()
adapter.Fill(dt)
For Each reader As DataRow In dt.Rows
stallStats = Get_Stall_Stats(reader("Track").ToString, CInt(reader("Stall")), CDbl(reader("Distance")))
If stallStats = "" Then
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = 999 WHERE Horse = '" & reader("Horse").ToString & "'")
Else
mSplit = Split(stallStats, ",")
win = mSplit(0)
MainForm.NonQuery("UPDATE DailyRatings SET StallWin = " & win & " WHERE Horse = '" & reader("Horse").ToString & "'")
End If
Next
cn.Close()

Oracle db-connection doesn't work with entity framework: A null was returned after calling the 'get_ProviderFactory'

I'm trying to use the Entity Framework to access my database. The connection itself does work, as shown in the sample. But if i try to access the DbSet in the context the code throws an Exception:
A null was returned after calling the 'get_ProviderFactory' method on
a store provider instance of type
'Oracle.DataAccess.Client.OracleConnection'. The store provider might
not be functioning correctly.
My context:
Public Class daDbContext
Inherits System.Data.Entity.DbContext
Public Property MyTable() As DbSet(Of MyTable)
Public Sub New(con As OracleConnection)
MyBase.New(CType(con, DbConnection), contextOwnsConnection:=True)
End Sub
End Class
Code-Sample:
Try
Dim con As New OracleConnection("Data Source=mydb;Password=mypassword;Persist Security Info=True;" + _
"User ID=myuser;Connection Lifetime=0;Connection Timeout=15;HA Events=false;" + _
"Load Balancing=false;Max Pool Size=100;Min Pool Size=1;Pooling=false;" + _
"Validate Connection=false")
con.Open()
' Try to read data with our connection'
Dim com As OracleCommand = con.CreateCommand()
com.CommandText = "SELECT * FROM mytable"
Dim reader As OracleDataReader = com.ExecuteReader()
While reader.Read()
Console.WriteLine("new connection: " + DirectCast(reader("mycolumn"), String))
' Works fine..'
End While
' Init ef-db-context'
Dim ctx As New daDbContext(con)
' Try to read Data with connection from ef-db-context'
com = DirectCast(ctx.Database.Connection.CreateCommand(), OracleCommand)
com.CommandText = "SELECT * FROM mytable"
reader = com.ExecuteReader()
While reader.Read()
Console.WriteLine("ctx connection: " + DirectCast(reader("mycolumn"), String))
' Works fine..'
End While
' Throws exception'
Dim l As List(Of MyTable) = ctx.MyTable.ToList()
Console.WriteLine(l.First().MyColumn)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
What am i doing wrong? I don't understand why the connection works while the ProviderFactory causes problems.

ADO Async Connection Cancellation Blocks

When I try to cancel an async ADO connection to some DB server that is offline (or not responding), the Cancel method of the ADODB.Connection object blocks for the set time-out period.
I do the async connection like this:
Set Connection = New ADODB.Connection
Connection.Provider = "SQLOLEDB"
Connection.ConnectionTimeout = 60
Connection.ConnectionString = "Initial Catalog=" & RTrim(DBName) & _
";Data Source=" & RTrim(DBServerName) & ";Integrated Security = SSPI"
Connection.Open , , , adAsyncConnect
And then later call the following to cancel/close the connection:
If (Connection.State And adStateConnecting) = adStateConnecting Then
' ==== CONNECTION BLOCKS HERE ======
Connection.Cancel
End If
If (Connection.State And adStateOpen) = adStateOpen Then
Connection.Close
End If
Set Connection = Nothing
Is there a way to not let the Cancel method block?
I found my own solution at the end. Well, at least an acceptable workaround.
First I created a module that could cancel/close the connection in a timer (thanks to an idea from a Code Project article):
Option Explicit
' Timer API:
Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, _
ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) _
As Long
Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, _
ByVal nIDEvent As Long) As Long
' Collection of connections to cancel
Private m_connections As Collection
' The ID of our API Timer:
Private m_lTimerID As Long
Private Sub TimerProc(ByVal lHwnd As Long, ByVal lMsg As Long, _
ByVal lTimerID As Long, ByVal lTime As Long)
On Error GoTo ErrH:
Dim cnx As ADODB.Connection
' Remove the timer
KillTimer 0, lTimerID
If Not m_connections Is Nothing Then
With m_connections
Do While .Count > 0
Set cnx = .Item(1)
.Remove 1
TryCancelOrCloseConnection cnx
Loop
End With
If m_connections.Count = 0 Then
Set m_connections = Nothing
End If
End If
' Let the next call to CancelOrCloseAsync create a new timer
m_lTimerID = 0
Exit Sub
ErrH:
' Let the next call to CancelOrCloseAsync create a new timer
m_lTimerID = 0
Debug.Print "Error closing connetions timer: " & Err.Description
End Sub
Private Sub TryCancelOrCloseConnection(cnx As ADODB.Connection)
On Error GoTo ErrH
If Not cnx Is Nothing Then
If (cnx.State And adStateConnecting) = adStateConnecting Then
' The call to Cancel here blocks this execution path (until connection time-out),
' but we assume it internally calls DoEvents, because (even though it blocks here) messages get pumped.
cnx.Cancel
End If
' If the connection actually made it to an open state, we make sure it is closed
If (cnx.State And adStateOpen) = adStateOpen Then
cnx.Close
End If
End If
Exit Sub
ErrH:
Debug.Print "ADO Connection Cancel/Close error " & Err.Description
' We deliberately suppress the error here.
' The reason is that accessing the Connection.State property, while there was an error when
' connecting, will raise an error. The idea of this method is simply to make sure we close/cancel
' the pending connection if there was no connection error.
End Sub
Public Sub CancelOrCloseAsync(cnx As ADODB.Connection)
If Not cnx Is Nothing Then
' Add cnx to the collection of connections to cancel
If m_connections Is Nothing Then
Set m_connections = New Collection
End If
m_connections.Add cnx
' Create a timer to start cancelling the connection(s), but only if one is not already busy
' We need to cast the process off to a timer because the Connection.Cancel blocks the
' normal execution path.
If m_lTimerID = 0 Then
m_lTimerID = SetTimer(0, 0, 1, AddressOf TimerProc)
End If
End If
End Sub
I then created a Connection surrogate class called clsADOAsyncConn
Private WithEvents Connection As ADODB.Connection
Private m_Pending As Boolean
Public Event ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
Public Property Get Provider() As String
Provider = Connection.Provider
End Property
Public Property Let Provider(ByVal val As String)
Connection.Provider = val
End Property
Public Property Get ConnectionTimeout() As Long
ConnectionTimeout = Connection.ConnectionTimeout
End Property
Public Property Let ConnectionTimeout(ByVal val As Long)
Connection.ConnectionTimeout = val
End Property
Public Property Get ConnectionString() As String
ConnectionString = Connection.ConnectionString
End Property
Public Property Let ConnectionString(ByVal val As String)
Connection.ConnectionString = val
End Property
Public Sub OpenAsync(Optional ByVal UserID As String = "", Optional ByVal Password As String = "")
Connection.Open , UserID, Password, adAsyncConnect
m_Pending = True
End Sub
Private Sub Class_Initialize()
Set Connection = New ADODB.Connection
End Sub
Private Sub Class_Terminate()
If Not Connection Is Nothing And m_Pending Then
' While the connection is still pending, when the user of this class reminates the refernce
' of this class, we need to cancel it in its own timer loop or else the caller's code will
' block at the point where the refernce to this object is de-referenced.
CancelOrCloseAsync Connection
End If
End Sub
Private Sub Connection_ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
m_Pending = False
' Notify the object client of the connection state
RaiseEvent ConnectComplete(pError, adStatus, pConnection)
End Sub
I then update my original connection code to this:
Set Connection = New clsADOAsyncConn
Connection.Provider = "SQLOLEDB"
Connection.ConnectionTimeout = 60
Connection.ConnectionString = "Initial Catalog=" & RTrim(DBName) & _
";Data Source=" & RTrim(DBServerName) & ";Integrated Security = SSPI"
Connection.OpenAsync
The actual connection is then retuned by the clsADOAsyncConn.ConnectComplete event.
The only known issue with this solution is that even though it helps prevent a block in normal execution of code, it still causes a block when the process exits (at least until the last pending connection(s) times out)

Trying to use a VB Script in an SSIS package to get the Server name from a connection

I'm writing an SSIS package that uses VB Scripts to send several confirmation emails. One of the confirmation emails needs to have the name of the server from one of the connection managers. I have a script doing the following:
Public Sub Main()
'------------- Set Vriables
Dim htmlMessageTo As String = _
Dts.Variables("Email_To").Value.ToString
Dim htmlMessageCC As String = _
Dts.Variables("Email_CC").Value.ToString
Dim htmlMessageFrom As String = _
Dts.Variables("Email_From").Value.ToString
Dim ServerAConnectionString As String = _
DirectCast(Dts.Connections("ServerA").AcquireConnection(Dts.Transaction), String)
Dim smtpConnectionString As String = _
DirectCast(Dts.Connections("Mail1").AcquireConnection(Dts.Transaction), String)
Dim smtpServerStr As String = _
smtpConnectionString.Split(New Char() {"="c, ";"c})(1)
Dim smtpServer As New SmtpClient(smtpServerStr)
Dim myMail As New MailMessage
With myMail
.From = New MailAddress(htmlMessageFrom)
.To.Add(htmlMessageTo)
If Len(htmlMessageCC) > 0 Then
.CC.Add(htmlMessageCC)
End If
.IsBodyHtml = True
.Subject = "The process failed for server " & ServerAConnectionString
.Body = "The process failed for server " & ServerAConnectionString
End With
smtpServer.Send(myMail)
Dts.TaskResult = ScriptResults.Success
End Sub
Where I'm trying to get just the server name of ServerA. And the connection manager Mail1 is the SMTP server I'm using. Everything is working fine for my other strings but this particular one gives me an error. I believe I can get the connection string but don't know enough VB to parse it. I'm hoping there is a way to get into the connection object and view the server name property, I just haven't been able to find it.
For ADO.NET connections:
Dim dbConn As System.Data.SqlClient.SqlConnection = Dts.Connections("ServerA").AcquireConnection(Dts.Transaction)
Dim dbServer As String = dbConn.DataSource
Dts.Events.FireInformation(-1, "", dbServer, String.Empty, -1, False)
It yields the following in the log:
[] Information: ServerName\InstanceName
OleDB connections are a bit trickier -- to access the same thing in a vb script, add a reference to the .NET assembly Microsoft.SqlServer.DTSRuntimeWrap, then use the following:
Dim connectionManager As ConnectionManager = Dts.Connections("oleDBConnection")
Dim cmParam As Microsoft.SqlServer.Dts.Runtime.Wrapper.IDTSConnectionManagerDatabaseParameters100
cmParam = CType(connectionManager.InnerObject, Wrapper.IDTSConnectionManagerDatabaseParameters100)
Dim conn As OleDb.OleDbConnection = CType(cmParam.GetConnectionForSchema(), OleDb.OleDbConnection)
Dts.Events.FireInformation(-1, "", conn.DataSource, String.Empty, -1, False)
Tested and verified reference: http://blogs.msdn.com/b/mattm/archive/2008/08/22/accessing-oledb-connection-managers-in-a-script.aspx

Unusually long pause trying to close ADO.NET connection object: is this normal?

I am performing a simple exercise of opening an SQL Server database connection, pulling the first record of a table from a DataReader object, and then closing the object. However, I have noticed that theres bit of a delay, about 5 seconds or so, in closing the connection. However, the delay only occurs after the command object executes the specified query. I've worked in a setup like this before and don't remember there being such a long delay while closing the connection.
Public Sub TestDb()
Dim cnStrBuilder As New SqlClient.SqlConnectionStringBuilder
Dim cn As New SqlClient.SqlConnection
Dim sqlSelectName As New SqlClient.SqlCommand
Dim drName As SqlClient.SqlDataReader
Dim newName As New SymName
Dim i As Integer
cnStrBuilder.UserID = "sa"
cnStrBuilder.ConnectTimeout = 30
cnStrBuilder.Password = ""
cnStrBuilder.PersistSecurityInfo = True
cnStrBuilder.DataSource = "EMARKET\FL_DB"
cnStrBuilder.InitialCatalog = "EmailMarketing"
sqlSelectName.CommandType = CommandType.Text
sqlSelectName.CommandText = "SELECT * FROM [NAME]"
System.Console.WriteLine(cnStrBuilder.ConnectionString)
cn.ConnectionString = cnStrBuilder.ConnectionString
Try
If cn.State = ConnectionState.Closed Then
cn.Open()
End If
System.Console.WriteLine("Connection success")
sqlSelectName.Connection = cn
System.Console.WriteLine("Execute Reader")
drName = sqlSelectName.ExecuteReader
If drName.HasRows = True Then
System.Console.WriteLine("Read Row")
drName.Read()
For i = 0 To drName.FieldCount - 1
Console.WriteLine(drName.Item(i).ToString)
Next
End If
System.Console.WriteLine("Closing connection")
sqlSelectName.Connection.Close()
Catch ex As Exception
System.Console.WriteLine("Something Happened")
System.Console.WriteLine(ex.Message)
End Try
System.Console.WriteLine("Done.")
End Sub
If I omit the lines
'System.Console.WriteLine("Execute Reader")
'drName = sqlSelectName.ExecuteReader
'
'If drName.HasRows = True Then
' System.Console.WriteLine("Read Row")
' drName.Read()
'
' For i = 0 To drName.FieldCount - 1
' Console.WriteLine(drName.Item(i).ToString)
' Next
'End If
The connection closes almost imediately. What gives? I have narrowed it down to the where the .ExecuteReader line that causes the delay in the connection close. Whats causing the delay and how do I resolve it?
You're telling SQL Server to retrieve the entire table. Yet after the first row, you stop, and close the connection. Like you, I would expect the connection to close immediately, but perhaps the server is busy spooling the table to a place where it can return your data quickly.
Does the connection still close slowly if you only ask the server for one row? F.e.
sqlSelectName.CommandText = "SELECT TOP 1 * FROM [NAME]"
Generally, you should be wrapping any objects that implement IDisposable in using statements which includes the connection object. I would try implementing something like the following which is from MSDN:
http://msdn.microsoft.com/en-us/library/y6wy5a0f.aspx#Y400
Public Sub CreateCommand(ByVal queryString As String, _
ByVal connectionString As String)
Using connection As New SqlConnection(connectionString)
Dim command As New SqlCommand(queryString, connection)
connection.Open()
Dim reader As SqlDataReader = _
command.ExecuteReader(CommandBehavior.CloseConnection)
While reader.Read()
Console.WriteLine("{0}", reader(0))
End While
End Using
End Sub

Resources