VB.net MySQL connection check not working / slow - mysql

I have a program that's main function is to pull data from a MySQL database every x amount of time and adjust the GUI accordingly.
I have a two part problem in that the first thing that happens is that my GUI loads incredibly slowly in the background whilst the connection is being tried, you can literally see each label/box/image loading one by one until the MySQL check is complete.
A shoddy fix would be to add a me.Hide() before the function and then make it reappear after the results of the check have been displayed. Can you please take a look at the code? Will adding the MySQL check to a function and then calling it on Form1_Load help?
The second part of this is that my MySQL connection checker doesn't seem to work, now my host is accepting remote MySQL sessions and it does seem to think about connecting for a while... It's a good 6/7 seconds before the connection error message appears.
I pretty much followed guides and wrote the code myself, I understand each part but I think perhaps my ConnectionString is invalid.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MaximizeBox = False
Dim conn As MySqlConnection
conn = New MySqlConnection
conn.ConnectionString = "Server=50.28.x.x; Port=3306; User id=xxx_admin; password=xxxx; Database=xxx_software; Connect Timeout=60;"
Try
conn.Open()
Catch myerror As MySqlException
MsgBox("There was an error connecting to the MySQL Database")
Label42.ForeColor = Color.Red
Label42.Text = "ERROR"
Timer1.Stop()
End Try
Timer1.Start()
Label37.Text = Now.ToShortTimeString()
End Sub

.Connect() is a blocking call, therefore you shouldn't put it into a sub that handles the Load event. Put it into Activated for example and check if it has run or not. You might also want to look into async/await which is officially available since VS2012. Though it's still not a good idea to put anything into Load that can cause an exception, because there is a problem with exception handling in this stage that occurs on 64bit machines running code in a 32bit version (to keep it simple).
The basic idea is to keep the UI-Thread (which handles the redrawing of your forms etc) free from "heavy" work, which means that the best results you will get when using some kind of "threading". And async/await is a very easy way to do it, because it keeps the usual program flow.
Public Class Form1
Private InitDone As Boolean = False
Private Async Sub Form1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
If Not InitDone Then
InitDone = True
' simulate some blocking call
Await Task.Run(Sub() Threading.Thread.Sleep(10000))
Me.Text = "Ready to go"
End If
End Sub
End Class
Async is a decorator which simply "allows" you to use the await keyword in a method. This is needed because await is/was not a reserved word in C# and otherwise it might break old code.
The Await means that after the task has been scheduled, the UI-thread will continue and can redraw, process events etc. When the inner methode finished (usually in its own thread), the UI-thread "jumps" to the line directly after Await and executs that code. Nice an easy way to keep your app responsive AND to maintain a (seemingly) linear flow.
There is much more on async/await of course and you might need to invest some time to get used to it, but I would guess that it's worth any minute invested!

Related

How to detect if your Microsoft Access application is compiled, i.e. running as an ACCDE

Does anyone have any VBA to detect if your Microsoft Access 2013/2016 application is running as an ACCDE, i.e. is compiled?
I would like my code to stop and break if an error happens and a) it's me running it (environ("username')) and if it's not an ACCDE.
Anyone have any suggestions?
Adapted from https://access-programmers.co.uk/forums/showthread.php?t=229474 and http://allenbrowne.com/ser-53.html
Public Function IsACCDE() As Boolean
' Init
IsACCDE = False
' This property exists only in compiled DBs (.mde, .accde)!
' Ignore error (and stay "False") if not.
On Error Resume Next
IsACCDE = (CurrentDb.Properties("MDE") = "T")
End Function
I couldn't find any properties that consistently gave me the correct answer. Then I realized that those approaches were all approximations to what I really wanted to know. Now I just ask directly if it is an accde.
Private Function InDevelopment() As Boolean
On Error Resume Next
InDevelopment = False
InDevelopment = (InStr(CurrentDb.Properties("Name"), "accde") = 0)
End Function
I use this which is part of Access VBA:
If IsCompiled = False Then
DoCmd.RunCommand (acCmdCompileAndSaveAllModules)
End If
I have a lot of things that depend on knowning if I am in DEV (accdb/mdb) or LIVE (accde/mde).
My method is to take the right most letter.
strFinalDir = Right$(Application.CurrentProject.Name, 1)
Now I use B or E. The rest is easy.
But this is not the optimal solution as a hacker can physically change the last letter of the database.
This would make my code do "different" things and potentially expose my DB.
I've tried If IsCompiled = False Then, but this did not work for me.
So as of now, I am still using
strFinalDir = Right$(Application.CurrentProject.Name, 1)
and hoping a hacker does not know to change my e to b.

DataAdapter is not closing connection by itself

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

Connecting an Access form to a SQL Server

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.

Detect Server or Network Disconnects

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.

vb.net data reader using text change event

I want to create a textbox which contents are validated every time the text in it changes. And an image is displayed beside the textbox if it is correct and another image shows up if its not correct. Pretty much like ajax.
Heres the method which I called from the class.
Public Sub read1()
cmd.CommandText = "SELECT * FROM testdb WHERE Name='" & name & "'"
Try
rdr = cmd.ExecuteReader
Dim n As String
If rdr.HasRows Then
n = rdr("Name").ToString
Form1.PictureBox1.Image = Image.FromFile("C:\wamp\www\Dropbox\Public\Check-icon.png")
Else
Form1.PictureBox1.Image = Image.FromFile("C:\wamp\www\Dropbox\Public\Delete-icon.png")
End If
Catch ex As Exception
And here the event which calls for the method:
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
x.name = TextBox1.Text
x.read1()
End Sub
End Try
End Sub
Problem is, the method seems to be only called once. And the first image that was displayed stays the same even after inputting a data which does not exist in the database.
I'm connecting this program to a mysql database using odbc. Can you help me determine whats the problem and how to fix it?
Heres the screenshot:
With a DataReader object, you have to call the Read method before you can access the row. I think it would need to look like the following. I am guessing at the VB.Net syntax and method name, but I am reasonably sure it would be simply rdr.Read.
If rdr.HasRows Then
rdr.Read
n = rdr("Name").ToString
Edit In addition, you probably need to close the DataReader. Some .NET data providers allow only a single reader to be open at a time on a given connection, and it seems likely that the ODBC provider is one of them. Without a specific call to close it, you would be relying on the garbage collector to clean it up:
rdr.Close
And while this has nothing to do with the question directly, you should probably use a parameterized query to avoid an SQL injection attack.