I have a Windows service which inserts into a database whenever there is a change in data from an OPC Server(KepServer). The service inserts correctly when I manually start it from the Services Task Manager of Windows 7. But the service only inserts one row of data when starting up on system startup, whereas the desired number of rows must be 20. Because it is Windows Service, it is difficult to debug and it is not giving any error as well.
jjag
OS:Windows 7;
DB:SQL Server Express;
Win Service:run under Administrator
My code:
Protected Overrides Sub OnStart(ByVal args() As String)
' Add code here to start your service.
'Create a new OPC Server object
ConnectedOPCServer = New OPCServer
ConnectedOPCServer.Connect("KEPware.KEPServerEx.V5")
Try
' Add the group and set its update rate
ConnectedServerGroups = ConnectedOPCServer.OPCGroups
ConnectedGroup = ConnectedServerGroups.Add("Test1")
Catch ex As Exception
' Error handling
End Try
' Set the update rate for the group
ConnectedGroup.UpdateRate = 400
' Subscribe the group so that you will be able to get the data change
' callbacks from the server
ConnectedGroup.IsSubscribed = True
ItemCount = 3
OPCItemIDs(1) = "Channel2.Device1.EndDate"
OPCItemIDs(2) = "Channel2.Device1.Material"
OPCItemIDs(3) = "Channel2.Device1.BatchSchedule"
ClientHandles(1) = 1
ClientHandles(2) = 2
ClientHandles(3) = 3
Try
' Establish a connection to the OPC item interface of the connected group
OPCItemCollection = ConnectedGroup.OPCItems
OPCItemCollection.DefaultIsActive = True
OPCItemCollection.AddItems(ItemCount, OPCItemIDs, ClientHandles, ItemServerHandles, ItemServerErrors)
Catch ex As Exception
' Error handling
End Try
EventLog1.WriteEntry("In OnStart")
End Sub
Public Sub ConnectedGroup_DataChange(ByVal TransactionID As Integer, ByVal NumItems As Integer, ByRef ClientHandles As System.Array, ByRef ItemValues As System.Array, ByRef Qualities As System.Array, ByRef TimeStamps As System.Array) Handles ConnectedGroup.DataChange
'This is my sub which inserts into database in the event of data change from OPC Server
Const NoOfItems = 3
Dim ObjOPCItem As OPCAutomation.OPCItem
Dim Array_Values(NoOfItems) As Object
For Each ObjOPCItem In OPCItemCollection
Array_Values(ObjOPCItem.ClientHandle) = ObjOPCItem.Value
Next
ObjOPCItem = Nothing
Dim sql As String
sql = "INSERT INTO BatchReport (BatchCode,BatchNumber)VALUES(#AV,#AVa);"
If Array_Values(3) < 20 Then
Try
connection.Open()
dataadapter.InsertCommand = New SqlCommand(sql, connection)
dataadapter.InsertCommand.Parameters.Add("#AV", SqlDbType.Int, 4).Value = Array_Values(1)
dataadapter.InsertCommand.Parameters.Add("#AVa", SqlDbType.Int, 4).Value = Array_Values(3)
dataadapter.InsertCommand.ExecuteNonQuery()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
connection.Close()
End If
End Sub
Related
I am currently working on a timetable program and currently have a DataGridView in which the user enters data and should be able to save that data so when the program is opened again that data is still there. I have tried many options but nothing seems to work. I currently have this code:
Try
Me.Validate()
Me.Daily_formatBindingSource.EndEdit()
Me.Daily_formatTableAdapter.Update(Me.CalenderD1DataSet.Daily_format)
MsgBox("Update successful")
Catch ex As Exception
MsgBox("Update failed")
End Try
This is a piece of code from one of my programs that saves data from a Datagridview into a database, you should get the idea:
For i = 0 To DG_add_stock.Rows.Count - 1
Dim stock_code As String = CStr(DG_add_stock.Rows(i).Cells("Code").Value)
Dim stock_desc As String = CStr(DG_add_stock.Rows(i).Cells("Description").Value)
Dim qty As String = CStr(DG_add_stock.Rows(i).Cells("Qty").Value)
Dim price As String = CStr(DG_add_stock.Rows(i).Cells("Price").Value)
Dim tax_rate As String = CStr(DG_add_stock.Rows(i).Cells("Tax_Rate").Value)
Dim line_total As String = CStr(DG_add_stock.Rows(i).Cells("Line_Total").Value)
''' Save the data in a database or update existing data
Next
And then to show the data:
DG_Maintenance_Stock.Rows.Clear()
''' Get the data from the database
For add_items = 0 To row_count - 1
''' Load values from each row
DG_Maintenance_Stock.Rows.Add(code_list, desc_list, qty_list, tax_rate_list, price_list)
DG_Maintenance_Stock.Sort(maintenance_stock_Code, System.ComponentModel.ListSortDirection.Ascending)
Next
Access 2003 / SQL Server - how can I update Access 2003 MDB (Connect property) to point to a different SQL Server database? The new SQL Server database is on the same instance as the old one.
I have several MS Access 2003/SQL Server applications that I manage. All of them dynamically attach to the correct database at startup. Some of them even connect to multiple databases on different servers during the start up sequence. All of them use the same basic vba routine to actually dynamically attach a table to the correct server. This is not my code, I found it by googling around the internet, but I have lost the reference to it now, so apologies in advance to the authors.
Before showing the code, to put it in context, I normally have a form "frmInitApp" with a data source that is a local config table, with a field named "ID". I start the access application from the AutoExec macro which opens this form with a filter of "ID=1". I have other forms to manipulate this config table and change the IDs around, so to switch between production and test I just change which entry has ID=1.
I also have another local table, tableList, with a list of Access tables that I want to connect dynamically to a SQL Server. Most applications have another field in this table for the SQL Server table name (so they don't have to be the same) - some applications have an additional field to specify which database. But the more complex the more other spaghetti you need - I often end up with another table of connection strings to all the separate databases I might connect to etc etc. To keep it simple just have the connection string in a field in the config table that is the datasource to frmInitApp.
We get started with the current event on frmInitApp.
Private Sub Form_Current()
If Me.Filter = "" Then 'If nobody has told us what record to use then use id=1
Me.Filter = "[ID]=1"
configID = 1
Else
configID = CInt(Mid(Me.Filter, 6)) 'We are assuming the load criteria are "[ID]=..."
End If
Me.messages = "Connecting to databases ..."
DoCmd.Hourglass True
Me.stage = "InitialStartup" 'Set the stage which is to be executed during timer phase
Me.TimerInterval = 100 'We set the time to go off to so we can let autoexec finish and let us control focus
End Sub
and then in the timer we can link to the tables via an attach table function with I'll put further down the answer. Note also that we relink pass through queries as well so they point to the new database also. Also note that we start Open a new form a login one fore users as soon as we have attached to the first table. I don't show the conclusion where will probably have to validate username and password against the attached table when its all done, but its trivial to figure out anyway.
Private Sub Form_Timer()
Dim conn As ADODB.Connection
Dim dbRs As ADODB.Recordset
Dim dbOK As Boolean
Dim SQL As String
Dim startedLogon As Boolean
Me.TimerInterval = 0
Select Case Me.stage
Case "InitialStartup"
Set conn = CurrentProject.Connection
startedLogon = False
If CurrentProject.AllForms("frmLogon").IsLoaded Then
'If its already loaded this NOT the first time through, but still need to logon ...
If Form_frmLogon.configID = configID Then
startedLogon = True 'unless its the same config
End If
End If
dbOK = True
Set dbRs = New ADODB.Recordset
dbRs.Open "SELECT localname,servername FROM tableList", conn
While dbOK And Not dbRs.EOF
'PLEASE NOTE - WHILST THEORETICALLY "localname" and "servername" could be different the migration process
'requires that they be the same. Do not consider changing this until after migration is completed
dbOK = AttachTable(dbRs("localname"), "dbo." & dbRs("servername"))
dbRs.MoveNext
If Not startedLogon And dbOK Then
DoCmd.Close acForm, "frmLogon" '#554 Just in case its alread open - we need to pick up new params
DoCmd.OpenForm "frmLogon", , , , , , Nz(Me.lastUserId, "") & ":" & configID
Form_frmLogon.SetFocus '#748 Give it focus
startedLogon = True
End If
Wend
dbRs.Close
If dbOK Then
Me.messages = "Relinking Common Queries ..."
DoEvents
Dim qd As DAO.QueryDef, cs As String
cs = getStrConnDAO 'get the DAO connection string
For Each qd In CurrentDb.QueryDefs
If Len(qd.Connect & vbNullString) > 0 Then
qd.Connect = cs
End If
Next
End If
Me.messages = "Awaiting User Log On"
DoCmd.Hourglass False
DoEvents
... the rest just managing logon
End Sub
The attached table function
'//Name : AttachTable
'//Purpose : Create a linked table to SQL Server without using a DSN
'//Parameters
'// stLocalTableName: Name of the table that you are creating in the current database
'// stRemoteTableName: Name of the table that you are linking to on the SQL Server database
Private Function AttachTable(stLocalTableName As String, stRemoteTableName As String)
Dim td As TableDef
Dim stConnect As String
Me.messages = "Connecting to Database Table " & Me.mainDatabase & "." & stRemoteTableName
DoEvents
On Error Resume Next
CurrentDb.TableDefs.Delete stLocalTableName
If Err.Number <> 0 Then
If Err.Number <> 3265 Then GoTo AttachTable_Err 'v4.0.44 - allow delete errors
Err.Clear
End If
On Error GoTo AttachTable_Err
Set td = CurrentDb.CreateTableDef(stLocalTableName, dbAttachSavePWD, stRemoteTableName, getStrConnDAO(configID))
CurrentDb.TableDefs.Append td
DoEvents
AttachTable = True
Exit Function
AttachTable_Err:
AttachTable = False
errMsg = "AttachTable encountered an unexpected error: " & Err.description & " on table " & stRemoteTableName & " in database " & Me.mainDatabase
End Function
You will need to getConStrDAO function
Private ADOconnStr As String
Private DAOconnStr As String
Public Function getStrConn(Optional configID As Long = 0) As String
'create a connection string for use when running stored procedures
'this uses the saved value if possible, but global variables are reset if an error occurs
If ADOconnStr = "" Then
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim account As String
Dim revealedPassword As String
Dim s As String, i As Integer, x As String
Set conn = CurrentProject.Connection
If configID = 0 Then configID = Nz(Form_frmLogon.configID, 0)
Set rs = conn.Execute("SELECT * FROM localConfig WHERE id =" & configID)
If Not rs.EOF Then
ADOconnStr = "Provider=Microsoft.Access.OLEDB.10.0;Data Provider=SQLOLEDB;SERVER=" 'this provider is needed to allow use of SP as form.recordset
ADOconnStr = ADOconnStr & rs("ServerName") & ";DATABASE=" & rs("DatabaseName") & ";UID="
ADOconnStr = ADOconnStr & rs("dbUser") & ";PWD=" & EncryptDecrypt(Nz(rs("dbPassword"), ""))
End If
rs.Close
Set rs = Nothing
Set conn = Nothing
End If
getStrConn = ADOconnStr
End Function
Public Sub resetConnection()
ADOconnStr = ""
DAOconnStr = ""
End Sub
Function getStrConnDAO(Optional configID As Long = 0) As String
If DAOconnStr = "" Then
Dim a As New ADODB.Connection
a.Open getStrConn(configID)
DAOconnStr = "ODBC;driver=SQL Server;" & a.Properties("Extended Properties") & ";"
Set a = Nothing
End If
getStrConnDAO = DAOconnStr
End Function
And finally a simple encryption of database password to make it not obvious to casual eyes - something again copied from the internet
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''' Comments: Performs XOr encryption/decryption on string data. Passing a
''' string through the procedure once encrypts it, passing it
''' through a second time decrypts it.
'''
''' Arguments: szData [in|out] A string containing the data to
''' encrypt or decrypt.
'''
''' Date Developer Action
''' --------------------------------------------------------------------------
''' 05/18/05 Rob Bovey Created
'''
Public Function EncryptDecrypt(szData As String) As String
Const lKEY_VALUE As Long = 215
Dim bytData() As Byte
Dim lCount As Long
bytData = szData
For lCount = LBound(bytData) To UBound(bytData)
bytData(lCount) = bytData(lCount) Xor lKEY_VALUE
Next lCount
EncryptDecrypt = bytData
End Function
I'm trying to write a VB.net WPF program, that will add custom notes to a customer. Intuit has me going in circles with their code examples, that won't convert from C# to VB.net.
Can someone show me a very simple sample that shows adding a customers name only? Leave out any error checking etc, I can figure that out. I just don't know how to write the connection to quickbooks and send the request. Maybe a simple form with a textbox and a button, with the functionality to put the name in the textbox, and click the button to add the customer name.
I have long been disgusted at the poor code samples from Intuit for Quickbooks SDK. Having said that, I still use them; after fixing the code they are quite useful.
You also need to add a reference to Intuit Quickbooks SDK QBFC7Lib; It might look slightly different depending on the version.
Here is what you need to do to make the code useful (use Edit > Quick Replace):
Replace // with ' (comments)
Remove all ; (semi-colons)
Replace == with =
Replace For j = 0 with For j as Integer = 0
Replace responseList.GetAt(i) with responseList.GetAt(j)
Remove trailing . (dot)
Replace Integerfor with Integer (next line) for
Replace DataExtRet with DataExtRet. (only when it shows an error)
Check for any other errors
Delete stuff you don't need
When you run it for the first time you need to have Quickbooks open and be ready to give permissions from Quickbooks. Here is a sample with the errors fixed.
Imports QBFC7Lib
Module QBSample
Public Class Customer
Public Sub DoCustomerAdd()
Dim sessionBegun As Boolean
sessionBegun = False
Dim connectionOpen As Boolean
connectionOpen = False
Dim sessionManager As QBSessionManager
sessionManager = Nothing
Try
'Create the session Manager object
sessionManager = New QBSessionManager
'Create the message set request object to hold our request
Dim requestMsgSet As IMsgSetRequest
requestMsgSet = sessionManager.CreateMsgSetRequest("US", 7, 0)
requestMsgSet.Attributes.OnError = ENRqOnError.roeContinue
BuildCustomerAddRq(requestMsgSet)
'Connect to QuickBooks and begin a session
sessionManager.OpenConnection("", "Your application")
connectionOpen = True
sessionManager.BeginSession("", ENOpenMode.omDontCare)
sessionBegun = True
'Send the request and get the response from QuickBooks
Dim responseMsgSet As IMsgSetResponse
responseMsgSet = sessionManager.DoRequests(requestMsgSet)
'End the session and close the connection to QuickBooks
sessionManager.EndSession()
sessionBegun = False
sessionManager.CloseConnection()
connectionOpen = False
WalkCustomerAddRs(responseMsgSet)
Catch e As Exception
MessageBox.Show(e.Message, "Error")
If (sessionBegun) Then
sessionManager.EndSession()
End If
If (connectionOpen) Then
sessionManager.CloseConnection()
End If
End Try
End Sub
Private Sub BuildCustomerAddRq(ByVal requestMsgSet As IMsgSetRequest)
Dim CustomerAddRq As ICustomerAdd
CustomerAddRq = requestMsgSet.AppendCustomerAddRq()
'Set field value for Name
CustomerAddRq.Name.SetValue("Test Customer 2")
'Set field value for IsActive
CustomerAddRq.IsActive.SetValue(True)
'Set field value for CompanyName
CustomerAddRq.CompanyName.SetValue("ab")
'Set field value for Salutation
CustomerAddRq.Salutation.SetValue("ab")
'Set field value for FirstName
CustomerAddRq.FirstName.SetValue("ab")
'Set field value for MiddleName
CustomerAddRq.MiddleName.SetValue("ab")
'Set field value for LastName
CustomerAddRq.LastName.SetValue("ab")
'Set field value for Addr1
CustomerAddRq.BillAddress.Addr1.SetValue("ab")
'Set field value for Addr2
CustomerAddRq.BillAddress.Addr2.SetValue("ab")
'Set field value for Addr3
CustomerAddRq.BillAddress.Addr3.SetValue("ab")
'Set field value for Addr4
CustomerAddRq.BillAddress.Addr4.SetValue("ab")
'Set field value for Phone
CustomerAddRq.Phone.SetValue("ab")
'Set field value for AltPhone
CustomerAddRq.AltPhone.SetValue("ab")
'Set field value for Fax
CustomerAddRq.Fax.SetValue("ab")
'Set field value for Email
CustomerAddRq.Email.SetValue("ab")
'Set field value for Contact
CustomerAddRq.Contact.SetValue("ab")
'Set field value for AltContact
CustomerAddRq.AltContact.SetValue("ab")
'May create more than one of these if needed
CustomerAddRq.IncludeRetElementList.Add("Name")
CustomerAddRq.IncludeRetElementList.Add("ListID")
End Sub
Private Sub WalkCustomerAddRs(ByVal responseMsgSet As IMsgSetResponse)
If (responseMsgSet Is Nothing) Then
Exit Sub
End If
Dim responseList As IResponseList
responseList = responseMsgSet.ResponseList
If (responseList Is Nothing) Then
Exit Sub
End If
'if we sent only one request, there is only one response, we'll walk the list for this sample
For j As Integer = 0 To responseList.Count - 1
Dim response As IResponse
response = responseList.GetAt(j)
'check the status code of the response, 0=ok, >0 is warning
If (response.StatusCode >= 0) Then
'the request-specific response is in the details, make sure we have some
If (Not response.Detail Is Nothing) Then
'make sure the response is the type we're expecting
Dim responseType As ENResponseType
responseType = CType(response.Type.GetValue(), ENResponseType)
If (responseType = ENResponseType.rtCustomerAddRs) Then
''upcast to more specific type here, this is safe because we checked with response.Type check above
Dim CustomerRet As ICustomerRet
CustomerRet = CType(response.Detail, ICustomerRet)
WalkCustomerRet(CustomerRet)
End If
End If
End If
Next j
End Sub
Private Sub WalkCustomerRet(ByVal CustomerRet As ICustomerRet)
If (CustomerRet Is Nothing) Then
Exit Sub
End If
'Go through all the elements of ICustomerRet
'Get value of ListID
Dim ListID1 As String
ListID1 = CustomerRet.ListID.GetValue()
'Get value of Name
Dim Name5 As String
Name5 = CustomerRet.Name.GetValue()
End Sub
End Class
End Module
Another problem I often see is the code jumping from a "retlist" to a "ret". This usually happens with queries e.g. ReceivePaymentQuery. It is not a problem with DoCustomerAdd. I only mention it here for reference when you are converting other methods.
The code from OSR:
Public Sub WalkReceivePaymentRet(ByVal ReceivePaymentRet As IReceivePaymentRetList)
If (ReceivePaymentRet Is Nothing) Then
Exit Sub
End If
Fix it like this:
Public Sub WalkReceivePaymentRet(ByVal ReceivePaymentRetList As IReceivePaymentRetList)
For a As Integer = 0 To ReceivePaymentRetList.Count - 1
Dim ReceivePaymentRet As IReceivePaymentRet
ReceivePaymentRet = ReceivePaymentRetList.GetAt(a)
If (ReceivePaymentRet Is Nothing) Then
Exit Sub
End If
Make sure to add "AddCu" into the Quickbooks by doing the following:
QB-> Edit -> Preferences-> Integrated Applications-> Company Preferences
Allow Access: ✓ | Application Name: AddCu
Imports Interop.QBFC13
Public Sub AddCu()
' Create a QBSessionManager object:
Dim SessionManager As QBSessionManager
SessionManager = New QBSessionManager
' Create an IMsgSetRequest object (the parameters specify US QuickBooks and the 6.0 spec):
Dim CustomerSet As IMsgSetRequest
CustomerSet = SessionManager.CreateMsgSetRequest("US", 6, 0)
' Create an ICustomerAdd object:
Dim customer As ICustomerAdd
customer = CustomerSet.AppendCustomerAddRq
' Set the ICustomerAdd object's field values:
customer.Name.SetValue("Joey Hamham")
customer.Phone.SetValue("315-253-2642")
customer.Email.SetValue("bla#gmail")
' The request message set for this example is complete
' Open a connection:
SessionManager.OpenConnection("App", "AddCu")
' Begin a session:
SessionManager.BeginSession("", ENOpenMode.omDontCare)
' Create a IMsgSetResponse object:
Dim Resp As IMsgSetResponse
' Call DoRequests, passing the IMsgSetRequest object that was populated with a
Resp = SessionManager.DoRequests(CustomerSet)
' Display the unprocessed QBXML in the response in a message box:
MsgBox(Resp.ToXMLString)
' End the session:
SessionManager.EndSession()
' Close the connection:
SessionManager.CloseConnection()
' Clear the QBSessionManager
SessionManager = Nothing
End Sub
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
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