I have an Access application that is used as a TimeClock interface and to lookup pricing information. It is used on touchscreen computers and also on a Dell Latitude ST tablet which uses wireless exclusively.
The backend is SQL Server R2 2008 on a local Windows 7 "server" computer. I recently converted the application over to use forms bound to ADO recordsets in hopes that the application would be more resilient in the event that a disconnect occurs. I'm using a single, global ADO connection object. The tablet computer is configured to stay on all the time but it seems to lose connection occasionally. We could spend time troubleshooting the device and the network to make it more stable. However, I have hopes of being able to write my Access applications so they can be used over the WAN/Internet which will basically make network stability out of the picture and out of my control. I have chosen instead to focus on making this particular application (a fairly small one) more friendly with database disconnects and interruptions.
I've programmed a global function that basically checks to see if the ADO connection is open/connected. However, if I disconnect the network, the connection object still shows that State = adStateOpen.
I initially programmed a Test function to test the connection by opening a basic, single-record recordset. However, if the server was unavailable, this took way too long and seemed to cause a precipitation of errors beyond just the Test function (possibly some bad coding). I even changed connection TimeOut settings but it appeared that those timeouts were ignored since my test function would take about 15 (or was it 30) seconds to return in the event that the server really was unavailable.
I've now changed my function to close the connection every time and then re-open it. It seems so terribly inefficient but it is the only way I have found to make my application work properly after a server disconnect, without having to close out and open the application again. Server disconnects or network interruptions are actually very uncommon (basically non-existent), especially on the wired machines.
Public Function IsGConOpen(Optional bOpenCon As Boolean = True) As Boolean
IsGConOpen = False
If bOpenCon = True Then
Dim sConString As String
sConString = Config.ADOConString
If sConString = "" Then
Call InitGlobalSettings
sConString = Config.ADOConString
End If
If sConString = "" Then Exit Function
If gCon Is Nothing Then
Set gCon = New ADODb.Connection
Else
If gCon.State = adStateOpen Then
gCon.Close
End If
End If
With gCon
.Provider = "MSDataShape"
.ConnectionString = sConString
.CursorLocation = adUseClient
.ConnectionTimeout = 5
.CommandTimeout = 5
.Open
End With
If gCon.State = adStateOpen Then
IsGConOpen = True
End If
Else
If gCon Is Nothing Then
IsGConOpen = False
ElseIf gCon.State = adStateOpen Then
IsGConOpen = True
End If
End If
End Function
Here's a similar question (VB6 instead of Access) on a different forum. It appears to me that no solution was reached.
Is there a better way to manage my global ADO connection and detect server/network interruptions without having to close out the connection and reopen it every time?
I always preferred carrying out a ping check on the server's IP to see if I had a connection, this way I can limit the amount of time to wait on my ping check to be as short as I like, I didn't originally write the following code but did adapt it to my needs:
Public Function SystemOnline(ByVal IPAdd As String) As Boolean
Dim oShell, oExec As Variant
Dim strText, strCmd As String
strText = ""
strCmd = "ping -n 2 -w 1000 " & IPAdd
Set oShell = CreateObject("WScript.Shell")
Set oExec = oShell.Exec(strCmd)
Do While Not oExec.StdOut.AtEndOfStream
strText = oExec.StdOut.ReadLine()
If InStr(strText, "Reply") > 0 Then
SystemOnline = True
Exit Do
End If
Loop
End Function
And then I call it as:
If Not SystemOnline("11.111.11.111") Then
'Disconnected
Else
'Connected
End If
Perhaps you could fire this at set intervals to see how the connection is looking.
ADO is just another interface to ODBC which does not have a connection status. In ODBC you have the simple approach that you connect, do stuff, if stuff errors deal with the error.
The way I got round this problem in my MS Access program was to :-
Create connection
Do things ( select, updated, insert & delete )
If things error repeat process once and then display the error if it is still there.
This would work for basic statments however you may need to expand it a little further if you are sending multiple statments such as :-
Connect "con.open"
Begin transaction "con.BeginTrans"
Inset rows "con.Execute"
If all rows OK Commit the transaction "con.Commit"
The rows are only stored in the taget ODBC data source when you Commit so even if you get a network disconnection part way through inserting your rows this would that the data would not be stored. You could keep looping through the above if it's an automatted process or stop with a user error if the issue needs to be resolved before the code continues.
Related
I have started to receive an error with my VB.NET application:
Timeout Expired. The timeout elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
I did have a few methods that were not correctly disposing of the connection. These have been fixed using Try and Finally.
However, this morning I received the error again whilst trying to open a simple report. I had MySQL Workbench open monitoring client connections. At the time I had 4 threads connected.
The MySQL DB kills connections that have been asleep for more than 15 seconds.
I am at a loss as to how I could have reached the max pool size, if indeed that is what the error is referring to.
Does the application read the max pool size setting and hold its own count of connections and throw the error when that number is reached, or does it get the number from the MySQL DB directly every time a new connection is opened?
Or could the error be due to something else?
EDIT 1
Some stats from MySQL Workbench
Threads Connected:3
Threads Running: 1
Threads Created: 250
Threads Cached: 5
Rejected (over limit): 0
Total Connections: 2822
Connection limit: 151
Aborted Clients: 2694
Aborted Connections: 84
Errors: 0
EDIT 2
Sample code calling and disposing of connection:
Public Shared Function GetCoursePaperCertificate(ByVal CourseTypeID As Integer) As String
Dim connx As New MySqlConnection(My.Settings.BMConnString)
Try
Dim cmdTextx = "Select `Cert` From `Courses` WHERE `ID`=#ID"
Dim cmdx As New MySqlCommand(cmdTextx, connx)
cmdx.Parameters.AddWithValue("#ID", CourseTypeID)
connx.Open()
Dim result = cmdx.ExecuteScalar
If result Is Nothing OrElse result Is DBNull.Value Then
Return String.Empty
Else
Return result
End If
Catch ex As Exception
Return String.Empty
Finally
connx.Dispose()
connx = Nothing
End Try
End Function
There are several things in your code.
First turn on Option Strict. The function is declared to return a string, but you are trying to return Object with Return result
Everything which implements a Dispose method ought to be used inside a Using block. This allows you to declare and initialize an object, use it and dispose of it at the end.
Parameters.Add is better than AddWithValue. The later forces the DB Provider to guess the datatype based on the data.
Depending on the load and whether that method is used a lot, you could load the data to a DataTable and do lookups on that rather than query the DB over and over.
The core issue is (probably) that you do not dispose of the DBCommand object. Look at the constructor you use:
Dim cmdx As New MySqlCommand(cmdTextx, connx)
The DBCommand object is passed a reference to the connection. Even though you explicitly dispose of the connection, cmdx still has a reference to it, and it was not disposed. Using blocks make it simple to be sure things are disposed:
Dim sql = "Select `Cert` From `Courses` WHERE `ID`=#ID"
Using dbCon As New MySqlConnection(MySQLConnStr)
Using cmd As New MySqlCommand(sql, dbCon)
cmd.Parameters.Add("#Id", MySqlDbType.Int32).Value = CourseTypeID
dbCon.Open()
Dim result = cmd.ExecuteScalar
If result Is Nothing OrElse result Is DBNull.Value Then
Return String.Empty
Else
Return result.ToString()
End If
End Using ' close, dispose of conn
End Using ' dispose of DBCommand
To reduce indentation, you can "stack" items into one Using block:
Using connx As New MySqlConnection(MySQLConnStr),
cmd As New MySqlCommand(sql, connx)
...
End Using
Note the comma at the end of the first line.
I'd be surprised if this was not the cause of your leak (of course all the code would need to be changed).
I have an Access 2010 front end which has multiple linked tables on a local MySQL server (Windows) connected via ODBC. Most of the time it works just fine however Access drops the connection if left for several hours with Access open but not being used. When a form is opened again I get an ODBC error.
The problem is fixed by closing Access and opening it again.
I'm guessing that it's a timeout issue but I'm not an Access expert. It seems that I need a way of checking for a valid ODBC connection and if necessary, re-connecting?
Any ideas?
Unfortunately there is no readily available way at this point in time to resolve this issue.
Often the issue is the result of power management on the computer. If the computer goes to sleep, or the computer puts the network card to sleep, then you in trouble and you have to re-start Access.
So if your computer sleeps, or puts the network card to sleep, you often loose the connection - once lost you have to re-start.
Also some hardware with windows 10 also has issues since the DEFAULT setting for windows 10 is fast start up (this means a copy of the kernel and MOST important a copy of the drivers loaded and initialized are written to disk and used to shorten boot/start up time).
If you seeing this issue on windows 10, then I would consider disabling fast start up as that will “always” force a cold re-initializing of the drivers (such as those for the network card). I would also consider turning off the “sleep” mode if that’s been determined to be an issue here (and that such offending computers are seeing this issue after a wake up).
Edit: I should also point out that some "test" for a ODBC connection will not fix this issue since as noted, once lost, you have to re-start Access.
You could try an old trick - at launch to open a simple form bound to a table and hide that form, and leave it open.
I rarely worked during several hours on MySQL tables so I never experienced this issue, but IMO it sounds like a driver-related issue or feature.
Waht I would do in this case is to refresh the tables' connections in VBA, looping over your TableDef objects.
Make a hidden form with a 1 hour timer for instance, and add the following code in the form_timer:
Sub Form_Timer()
Dim tDef As DAO.TableDef
Dim strConString As String
Dim strMySQLServer As String
Dim strMySQLDB As String
Dim strMySQLUser As String
Dim strMySQLPass As String
' ADAPT THIS :
strMySQLServer = ""
strMySQLDB = ""
strMySQLUser = ""
strMySQLPass = ""
strConString = "ODBC;DRIVER={MySQL ODBC 5.1 Driver};Server=" & strMySQLServer & ";Database=" & strMySQLDB & ";Uid=" & strMySQLUser & ";Pwd=" & strMySQLPass & ";Option=3"
For Each tDef In CurrentDb.TableDefs
' You probably have to adapt the folowing if
' Debug and inspect the connection string of your actual MySQL Linked tables
If Left(tDef.Connect, 4) = "ODBC" And InStr(tDef.Connect, "MySQL") > 0 Then
tDef.Connect = strConString
tDef.RefreshLink
End If
Next tDef
End Sub
Watch my comments carefully, espacially if you have linked table to several MySQL databases. It will break some of them without adaptation.
You might also have to adapt the connection string following your MySQL driver, or to use the DSN entry that you specified in the ODBC control panel instead.
As we know that DataAdapter opens and closes a Connection if it is not already open. But with my code it opens but not close..I am using MySql.Data.MySqlClient.MySqlDataAdapter, not sure what I am doing wrong. below is my code
Public Function GetDT(ByVal SqlQuery As String, ByVal ConString As String) As DataTable
Dim da As New MySql.Data.MySqlClient.MySqlDataAdapter(SqlQuery, ConString)
Dim ds As New DataSet
da.Fill(ds)
GetDT = ds.Tables(0)
da.Dispose()
ds.Dispose()
da = Nothing
ds = Nothing
End Function
I am using this connection string:"server=localhost;port=3306;user=someuser;pwd=somepassword;database=mydatabasename;Allow Zero Datetime=True;"
while debugging the code I found as soon as DataAdapter.fill executes its start a connection thread, but no where connection is getting close as thread remains in SLEEP state . Please check the below image.
Can any one please help me on this ?
Thanks
Its normal behavior because ADO.NET uses Connection Pooling with SQL Server by default. From MSDN:
http://msdn.microsoft.com/en-US/library/8xx3tyca(v=vs.110).aspx
Connecting to a database server typically consists of several
time-consuming steps. A physical channel such as a socket or a named
pipe must be established, the initial handshake with the server must
occur, the connection string information must be parsed, the
connection must be authenticated by the server, checks must be run for
enlisting in the current transaction, and so on.
In practice, most applications use only one or a few different
configurations for connections. This means that during application
execution, many identical connections will be repeatedly opened and
closed. To minimize the cost of opening connections, ADO.NET uses an
optimization technique called connection pooling.
Connection pooling reduces the number of times that new connections
must be opened. The pooler maintains ownership of the physical
connection. It manages connections by keeping alive a set of active
connections for each given connection configuration. Whenever a user
calls Open on a connection, the pooler looks for an available
connection in the pool. If a pooled connection is available, it
returns it to the caller instead of opening a new connection. When the
application calls Close on the connection, the pooler returns it to
the pooled set of active connections instead of closing it. Once the
connection is returned to the pool, it is ready to be reused on the
next Open call.
....
The connection pooler removes a connection from the pool after it has
been idle for approximately 4-8 minutes,...
The effect of "using" or "dispose" in the DataAdapter is the same. "using" ensures that dispose is executed, even if an exception happends. Is the same as using a try / finally statment and put the "dispose" on the finally section.
The connection only closes itself when you use a Using directive, the Using statement can be used on any object that implements the IDisposable interface.
In the Dispose() method of the DataAdapter the connection is closed before disposing the object.
using it correctly would be something like this.
Public Function GetDT(ByVal SqlQuery As String, ByVal ConString As String) As DataTable
Using da As New MySql.Data.MySqlClient.MySqlDataAdapter(SqlQuery, ConString)
Dim ds As New DataSet
da.Fill(ds)
GetDT = ds.Tables(0)
da.Dispose() //because you are now using a 'Using' statement, this method is not necessary anymore.
ds.Dispose()
da = Nothing
ds = Nothing
End Using
End Function
I have been working on a project that is near completion but this final part is causing me some headaches because I have never done this before and I am having a hard time finding the research to help get me going I have no idea where to even begin. The project is as follows: (this is the context of the email from my boss)
You will need to look up how to check for connectivity using vba. The server you will connect to is the HRLearnDev. I have an access form I will send you that has the connection info in macros on it, where I did something similar. The difference is, my program connected directly to the server. Yours will need to write to a local table, do a check for connectivity when the access file is opened, or every few minutes or something, and if the check reads that there is a connection, write the data from the local server table to the remote server table, then truncate the local table.
I am really stuck and I am running out of options so any insight on where to start to look that is for beginners would be greatly appreciated.
Try something like this to check the connection:
Public Sub TestConnection()
Dim cnn As ADODB.Connection
Dim canConnect As Boolean
Set cnn = New ADODB.Connection
cnn.Provider = "sqloledb"
cnn.Open "Data Source=HRLearnDev;Initial Catalog=YourTableName;UserID=userID;Password='password';"
If cnn.State = adStateOpen Then
canConnect = True
cnn.Close
End If
MsgBox "Can you connect? - " & canConnect
End Sub
Honestly, this is obviously a homework problem and I don't think giving you the entire answer would be the right thing to do, but that should get you started.
I'm setting up my first locally hosted website on a new machine in a long time and to be honest am not very familiar with all the manual settings that get configured now. My last set up was on a Windows XP box and it seemed to "just work" when I enabled IIS. Not so this time.
My latest issues is with connecting to a MSAccess DB. I have a VERY BASIC page with a form that contains 1 input. I check the DB to make sure it doesn't already contain the value and if not, then write the value to the DB. I get the following error:
Provider error '80004005'
Unspecified error
/trips/admin_add_hotel.asp, line 22
I've researched online and everything seems to point to it being a permissions issue. However, I can remove the second portion of the code (the writing) and the page will return fine. If I try to run the page again or refresh, I'll then get the error. This to me says the user has read access. Now, I can wait a little bit and remove the first portion of the code and just write to the DB and it will work fine. Again, to me signifying that the user has write access. However, if I try to run it again... same error.
Here's the ASP code:
<!--#include file="includefile.asp"-->
<%
if Request("action") = "submit" then
'make sure hotel name is filled in
if Request("fhotel") <> "" then
fhotel = Request("fhotel")
'make sure hotel doesn't already exist in DB
sql = "SELECT hotel_id FROM hotels WHERE hotel_name = '" & Replace(fhotel, "'", "''") & "'"
RS.Open sql, ConnectString
if NOT RS.EOF then
Session("msg") = "Hotel already exists"
End if
RS.Close
'add hotel to DB
if Session("msg") = "" then
sql = "INSERT INTO hotels (hotel_name) VALUES ('" & Replace(fhotel, "'", "''") & "')"
Conn.Open ConnectString
Conn.Execute sql
Conn.Close
Session("msg") = "Hotel added successfully"
fhotel = ""
End if
Else
Session("msg") = "Hotel left blank"
End if
Set RS = Nothing
Set Conn = Nothing
End if
%>
My include file is:
<%
ConnectString = "DSN=ConnectionDSN; Uid=username; Pwd=password"
Set RS = Server.CreateObject("ADODB.Recordset")
Set Conn = Server.CreateObject("ADODB.Connection")
%>
My DB has 1 table with two columns:
hotel_id [AUTONUMBER]
hotel_name [TEXT (255)]
Here are my system details:
Windows 7 Pro 64-bit (although connections have been set up for 32-bit based on what I've found online)
IIS 7.5
Just to make sure it wasn't a permissions issue, I've temporarily allowed the 'Everyone' user full access to that folder.
Any help is appreciated. I believe this is the last hurdle I have to get thing fully up and running.
Thanks in advance.
A few things:
32bit vs 64bits: make absolutely sure that your application is set explicitly to x86, not AnyCPU. Also make sure that if you are using the newer .accdb you have the Access Database Engine drivers installed.
Permissions: as Dan Matheus said, make sur that the process under which IIS runs has write permission to the folder. It may not be enough that you set it to Everyone, make sure that your web app can actually create a file in that folder (test this assumption in your code).
Make sure your connection string for the Access file explicitly mentions shared access, and not exclusive access, otherwise, if different processes have the
Keep a connection to the database open all the time to avoid issues with the quick creation/deletion of the lock file.
The Jet/ACE database driver needs to create the lock file every time you access the file (if it is in shared mode). Issues will arise when your app quickly access/releases the database file and the lock file is created/deleted too often.
First it will greatly slow down your performance, and second you will get strange errors.
Just keep a connection open to the database and close it when your app shutdowns.
For more information about the points above, see this:
Intermittent “System resource exceeded” exception for OleDB connection to Microsoft Access data file