How could I go about running access querys on a seperate thread? - vb.net

I have an app that uses querys for most of its functions, and when the query executes the app ui "freezes". It loads fairly quickly but as it happens many times it gets annoying quick. My app pretty much always uses a class sub to execute the querys. Is there a way to run that class sub on another thread and just return the datatable to the main thread? As im still inexperienced, i never touched threading or the backgroundworker, but from a bit of testing it gives and exception for cross thread operation not valid, and just generally i havent found any good info on how to set up threading for access. This is my sub on the class that executes the query :
Public Sub AddParams(Name As String, value As String, Optional datatype As OleDb.OleDbType = OleDbType.WChar)
Dim NewParam As New OleDbParameter(Name, datatype)
NewParam.Value = value
Params.Add(NewParam)
End Sub
Public Sub ExecuteQuery(query As String)
RecordCount = 0
Exception = ""
Try
DBconnection.Open()
DBcmd = New OleDbCommand(query, DBconnection)
Params.ForEach(Sub(p) DBcmd.Parameters.Add(p))
Params.Clear()
DBdatatable = New DataTable
DBdataadapter = New OleDbDataAdapter(DBcmd)
RecordCount = DBdataadapter.Fill(DBdatatable)
Catch ex As Exception
Exception = ex.Message
End Try
If DBconnection.State = ConnectionState.Open Then DBconnection.Close()
End Sub
Thank you.
Edit:
After some trying i still cant figure it out so i thought id just post my code after Jimi's suggestion to modify it to be async:
Public Async Function ExecuteQueryAsync(query As String) As Task(Of DataTable)
Dim dt As DataTable = New DataTable
RecordCount = 0
Exception = ""
Try
Using dbconn As New OleDbConnection([my connectionstring])
DBcmd = New OleDbCommand(query, dbconn)
Params.ForEach(Sub(p) DBcmd.Parameters.Add(p))
Params.Clear()
Await dbconn.OpenAsync
dt.Load(Await DBcmd.ExecuteReaderAsync)
End Using
Catch ex As Exception
Exception = ex.Message
End Try
If DBconnection.State = ConnectionState.Open Then DBconnection.Close()
Return dt
End Function
And here is the code that uses the query in a usercontrol:
Private Async Sub Search(Nev As String)
Access.AddParams("#nev", "%" & Nev & "%")
dgv.DataSource = Await Access.ExecuteQueryAsync("SELECT * from DT_ugyfelek WHERE Név LIKE #nev")
Dim f As notification = New notification
If NotEmpty(Access.Exception) Then f.SetAlert(Access.Exception, notification.AlertTypeEnum.Error) : Exit Sub
dgv.ColumnHeadersHeight = 75
dgv.Columns(0).Visible = False
dgv.Columns(1).Width = 230
dgv.Columns(1).HeaderText = "Név"
'i also set 10 other headertexts here
End Sub
Private Async Sub search_btn_Click(sender As Object, e As EventArgs) Handles search_btn.Click
Search(search_txtbx.Text)
End Sub
By the way it loads about 20k rows. Honestly all my intention with this is to have a loading gif or something like that so the user would know that something is happening.

Related

How to make your displayed data vary once this form pops up again?

I am new to VB.net and I really had a hard time to this problem. I want to fetch all rows where the statement is "Logical Reasoning" by displaying it one by one in my richtextbox and radio buttons once this form show up again through next button. I am using xampp for my database and visual studio vb.net. I hope you can help me with this. Thank you in advance. Here is my code in my database connection module:
Private dbConn As New MySqlConnection("datasource=localhost;port=3306;username=root;password=;database=quiz;Convert Zero DateTime=True")
Private qreader As MySqlDataReader
Private cmd As MySqlCommand = New MySqlCommand
Public Sub displayData(ByRef richText As RichTextBox, ByRef radiobtnA As RadioButton, ByRef radiobtnB As RadioButton, ByRef radiobtnC As RadioButton, ByRef radiobtnD As RadioButton, ByRef sql As String)
Try
cmd.Connection = dbConn
openConnection()
cmd.CommandText = sql
qreader = cmd.ExecuteReader(CommandBehavior.CloseConnection)
While qreader.Read
richText.Text = (qreader("Question").ToString)
radiobtnA.Text = (qreader("Choice A").ToString)
radiobtnB.Text = (qreader("Choice B").ToString)
radiobtnC.Text = (qreader("Choice C").ToString)
radiobtnD.Text = (qreader("Choice D").ToString)
End While
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error")
End Try
qreader.Close()
closeConnection()
End Sub
ReadOnly Property getConnection() As MySqlConnection
Get
Return dbConn
End Get
End Property
Sub openConnection()
If dbConn.State = ConnectionState.Closed Then
dbConn.Open()
End If
End Sub
Sub closeConnection()
If dbConn.State = ConnectionState.Open Then
dbConn.Close()
End If
End Sub
End Module ```
Here is the code in my form that I want to display the data with:
```Public Class quizForm
Dim sql As String
Private Sub quizForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
sql = "SELECT * FROM `examquestion` WHERE `Exam Category` = 'Logical Reasoning'"
displayData(rtxtQuestion, rbnA, rbnB, rbnC, rbnD, sql)
End Sub```
The code is working but only displayed the first row of category Logical Reasoning.

How do i get the data to my database using vb.net (class, module and form)

I hope the title is enough to understand my problem, I already installed whats need to run the ADO.NET, I already have a connection string in my module and data query in my class,
Imports System.Data
Imports System.Data.OleDb
Module GlobalVariables
Public sGlobalConnectionString As String
Friend conString As String
Public dr As OleDbDataReader
Sub Main()
Dim sGlobalConnectionString As New OleDb.OleDbConnection
Dim sDataserver As String
Dim sDatabaseName As String
Dim sDatabaseConnection As String
sDataserver = "localhost"
sDatabaseName = "employee"
sDatabaseConnection = "Driver={MariaDB ODBC 3.1 Driver}; SERVER=" & sDataserver & "; UID=root;PWD=******; Database=" & sDatabaseName & "; PORT=3307; OPTION=3"
sGlobalConnectionString = New OleDb.OleDbConnection(conString)
End Sub
End Module
this is my class
Imports System.Data.OleDb
Public Class clsDataQuery
Public Shared Sub Class_initialize()
Dim con = New OleDb.OleDbConnection
con.ConnectionString = sGlobalConnectionString
con.Open()
End Sub
Public Shared Sub Class_Terminate()
Dim con = New OleDb.OleDbConnection
If Not con Is Nothing Then
con.Close()
con = Nothing
End If
End Sub
Public Function GetRecordDataSet(ByVal sStoreProcName As String, ByVal sParameterList As String)
Dim cmd As New OleDbCommand()
Dim arrParameter, arrParamName
Dim sParamName As String
Dim sDataValue
Dim lCtr As Long
On Error GoTo errhandler
cmd.Connection = New OleDb.OleDbConnection
cmd.CommandTimeout = 1800
cmd.CommandText = CommandType.Text
If Not Trim(sParameterList) = "" Then
arrParameter = Split(sParameterList, "|", , vbTextCompare)
If UBound(arrParameter) >= 0 And IsArray(arrParameter) Then
For lCtr = 0 To UBound(arrParameter)
arrParamName = Split(arrParameter(lCtr), "$", , vbTextCompare)
sParamName = arrParamName(0)
sDataValue = arrParamName(1)
cmd.Parameters.Item(sParamName) = sDataValue
Next lCtr
End If
End If
GetRecordDataSet = cmd.ExecuteReader
cmd = Nothing
Exit Function
errhandler:
MessageBox.Show("Records Not Found!!!")
End Function
End Class
if this button is click, the value of Textbox1.text will search in the database if it is exist, if exist it will continue into another form if not error message will appear, how do i do that?
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim username = txtbox_lastname.Text
If username <> "" Then
Try
clsDataQuery.Class_initialize()
Catch ex As Exception
MessageBox.Show("No Record Found")
End Try
Else
MessageBox.Show("No Record Found!!!")
End If
End Sub
If this is MariaDb then you want to use the provider for MySql. Not ODBC and not OleDb. No wonder you are having problems. There is not much information available for this database compared to the usual Access database used for beginners.
Do not declare database objects anywhere but the method they are used in. You can declare a Private variable for the connection string at the class level.
Although one of these is a Module level variable and one is a local variable, it is very confusing and bad practice. Why would you call a connection object a String?
Public sGlobalConnectionString As String
Dim sGlobalConnectionString As New OleDb.OleDbConnection
BTW, it is fine to declare and initialize your variables in a single line.
Dim sDataserver = "localhost"
You create a new connection on the first line of Sub Main, then you throw it away and create another new connection on the last line. Since sDataServer and sDatabaseName are hard coded why not just put the literal values directly into the connection string.
After all that you pass conStr to the constructor of the connection instead of sDatabaseConnection. Why were you building sDatabaseConnection (another misnomer, it is not a connection, its a string) and then never use it. Has conStr been set elsewhere?
At any rate, throw out the whole module.
Moving on to your DataQuery class. First, the name should begin with an upper case letter.
Get rid of Class_initialize. We don't want to create or open any connection except in the method where it is used. You never call Class_Terminate so dump that too.
The GetRecordDataSet method...
Functions in vb.net require a datatype. The old VB6 syntax of assigning the return value to the name of the function will work but it is not the .net way. In vb.net we use the Return keyword.
You have not initialized or given a datatype to arrParameter, arrParamName or sDataValue which violates Option Strict. (You do have Option Strict On, don't you?)
On Error GoTo errhandler is a sad leftover from VB6. .net languages have structured error handling with Try...Catch...Finally...End Try.
cmd.Connection = New OleDb.OleDbConnection sets the connection property however this new connection has no connection string.
cmd.CommandText = CommandType.Text Now this is just silly. What I think you want is cmd.CommandType =CommandType.StoredProcedure
Using...End Using blocks take care of declaring, closing and disposing database objects even if there is an error. You don't want to return a DataReader because a reader requires an open connection. cmd.ExecuteReader returns a DataReader. I used the reader to load a DataTable and returned the DataTable.
It seems you are trying to develop a factory pattern but it is way too advanced for your. Just pass value and call a method specific to what your are searching for. You want your DataQuery code to be independent from your user interface. The Button code doesn't care where the data is coming from. It could be a database, a text file, or a web service. Likewise the DataQuery code doesn't know where the values are coming from. It could be a WinForms app, a web application or a phone app.
Public Class DataQuery
Private Shared ConStr As String = "server=localhost;userid=root;database=employee"
Public Shared Function SearchByLastName(ByVal LName As String) As DataTable
Dim dt As New DataTable
Using cn As New MySqlConnection(ConStr),
cmd As New MySqlCommand("Select * From PutTableNameHere Where LastName = #LName", cn)
cmd.Parameters.Add("#LName", MySqlDbType.VarChar).Value = LName
cn.Open()
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
Return dt
End Function
End Class
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim dt As DataTable = Nothing
If txtbox_lastname.Text <> "" Then
Try
dt = DataQuery.SearchByLastName(txtbox_lastname.Text)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End If
DataGridView1.DataSource = dt
End Sub
Before answering, I really think that the GetRecordDataSet() at the very least a darn good tidy up, better yet removed from history
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim username = txtbox_lastname.Text
If username <> "" Then
Try
clsDataQuery.Class_initialize()
Dim reader = GetRecordDataSet("storedProcName", txtbox_lastName.Text)
Do While reader.Read()
'Process record etc
Loop
Catch ex As Exception
MessageBox.Show("No Record Found")
End Try
Else
MessageBox.Show("No Record Found!!!")
End If
End Sub
Might be a bit rough, but should get you heading in the right direction

Load data from SQL Server to datagridview using a thread

I'm trying to load data from a SQL Server database into a DataGridView using threading with delegates and later will try to add a loading or waiting form. But I can't make it work.
Dim t1 As Thread
Private Delegate Sub delegate_updatedgv()
Private Sub mainform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
t1 = New Thread(AddressOf load_dgv)
t1.Start()
End Sub
Private Function load_dgv()
Try
If DBCon.State = ConnectionState.Closed Then
DBCon.Open()
End If
DBCmd = New SqlCommand("SELECT * FROM Jobs ", DBCon)
DBCmd.ExecuteNonQuery()
Dim DBDT = New DataTable
Dim DBDA = New SqlDataAdapter(DBCmd)
Dim RecordCount = DBDA.Fill(DBDT)
assignit(DBDT)
DBCon.Close()
Catch ex As Exception
MsgBox(ex.Message)
Finally
If DBCon.State = ConnectionState.Open Then DBCon.Close()
End Try
End Function
Sub assignit(ByVal x As DataTable)
If Me.InvokeRequired Then
Me.Invoke(New delegate_updatedgv(AddressOf assignit))
End If
GridControl1.DataSource = x
End Sub
and its not working at all
Your delegate
Private Delegate Sub delegate_updatedgv()
and your method
Sub assignit(ByVal x As DataTable)
don't have the same signature. Actually the compiler already tells you this in an error message...
You could just use Action(Of DataTable). There is no need to create a new delegate.
You also needed to set the DataSource only when invoking on the UI thread. You can achieve that by using Else.
Sub assignit(ByVal x As DataTable)
If Me.InvokeRequired Then
Me.Invoke(New Action(Of DataTable)(AddressOf assignit))
Else
GridControl1.DataSource = x
End If
End Sub
Alternately, you can also declare the delegate properly. Either way
Private Delegate Sub delegate_updatedgv(x As DataTable)
Also, the method / function Private Function load_dgv() has no return type, and nothing is returned. So it is better suited as a sub.
Private Sub load_dgv()
' ...
End Sub
The compiler also warns you about this.
Pay attention to compiler error messages!
While we are at, let's fix up the database code a bit. There is no need to check connection state when database objects are keep local to the method where they are used. This way you can ensure that they are closed and disposed with a Using...End Using block.
I have no idea why you are calling .ExecuteNonQuery on this command. You never use RecordCount so I don't see the point of that either. I just used one line to load the DataTable. The adapter seemed extraneous.
Private Sub load_dgv()
Dim dt As New DataTable
Using DBCon As New SqlConnection("Your connection string"),
DBCmd As New SqlCommand("SELECT * FROM Jobs ", DBCon)
DBCon.Open()
dt.Load(DBCmd.ExecuteReader)
End Using
assignit(dt)
End Sub
I know this didn't answer your threading question but I hope it helped for data access.
Where is the bottleneck in this code? Is it just the retrieval of data or is it updating the UI? Maybe only the data access needs to run on a separate thread.

SQLite Query is not working properly - can't load datatable

I am trying to write a function that queries the sqlite db I have. For some reason it does not work properly. Below are the supporting functions that I am using. I can get it to add tables perfectly fine.
Private Sub GetSqlConnection()
Me.SQLConnectionString = New SQLiteConnectionStringBuilder
Me.SQLConnectionString.DataSource = Path.Combine(Application.StartupPath, "mydb.sqlite")
Me.SQLConnectionString.Version = 3
SQLConnection = New SQLiteConnection(Me.SQLConnectionString.ConnectionString)
End Sub
Private Sub Query(ByVal SQLString As String)
Try
Dim SQLiteDRObj As SQLiteDataReader
Dim ResultsTableObj As DataTable = Nothing
Dim ResultSet As DataSet = Nothing
Dim SQLAdapter As SQLiteDataAdapter = Nothing
Me.GetSqlConnection()
SQLConnection.Open()
SQLCommand = New SQLiteCommand(SQLConnection)
SQLCommand.CommandText = SQLString
SQLiteDRObj = SQLCommand.ExecuteReader()
ResultsTableObj.Load(SQLiteDRObj)
SQLiteDRObj.Close()
SQLConnection.Close()
SQLConnection.Dispose()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
object shows as filled http://josephberardi.com/stackoverflow/objfilled.png
object shows as filled http://josephberardi.com/stackoverflow/exception.png
ResultsTableObj is Nothing when you call the Load method
Change this line to
Private Sub Query(ByVal SQLString As String)
Try
....
Dim ResultsTableObj As DataTable = New DataTable()
....

Multithreading Safe Calls not populating comboboxes vb.net

just curious on what im doing wrong here, the principle should work. Can anyone give me a hand?
The Code runs fine, but seems to not add them into my comboboxes
normal thread start like so
t1 = New Thread(New ThreadStart(AddressOf GetNewClientData))
t1.Start()
data is not empty or null... :)
Function GetNewClientData()
Try
Dim con As New SqlConnection
Dim myConString As String = My.Settings.ConString
Dim objcommand As SqlCommand = New SqlCommand
With objcommand
.Connection = con
Dim cmdText As String = "SELECT distinct Applicant,Client,Market,Project from AAClient order by Client"
.CommandText = cmdText
End With
con.ConnectionString = myConString
con.Open()
Using readerObj As SqlClient.SqlDataReader = objcommand.ExecuteReader
'This will loop through all returned records
While readerObj.Read
addClientInvoke(readerObj("Client").ToString)
addApplicantInvoke(readerObj("Client").ToString)
addMarketInvoke(readerObj("Client").ToString)
addProjectInvoke(readerObj("Client").ToString)
End While
End Using
con.Close()
Catch ex As Exception
End Try
Return Nothing
End Function
Delegate Sub addApplicant(s As String)
Sub addApplicantInvoke(ByVal s As String)
If CreateNewSite.cbApplicant.InvokeRequired Then
Dim d As New addApplicant(AddressOf addApplicantInvoke)
CreateNewSite.cbApplicant.Invoke(d, New Object() {s})
Else
CreateNewSite.cbApplicant.Items.Add(s)
End If
End Sub
Delegate Sub addClient(s As String)
Sub addClientInvoke(ByVal s As String)
If CreateNewSite.cbClient.InvokeRequired Then
Dim d As New addClient(AddressOf addClientInvoke)
CreateNewSite.cbClient.Invoke(d, New Object() {s})
Else
CreateNewSite.cbClient.Items.Add(s)
End If
End Sub
Delegate Sub addMarket(s As String)
Sub addMarketInvoke(ByVal s As String)
If CreateNewSite.cbMarket.InvokeRequired Then
Dim d As New addMarket(AddressOf addMarketInvoke)
CreateNewSite.cbMarket.Invoke(d, New Object() {s})
Else
CreateNewSite.cbMarket.Items.Add(s)
End If
End Sub
Delegate Sub addProject(s As String)
Sub addProjectInvoke(ByVal s As String)
If CreateNewSite.cbProject.InvokeRequired Then
Dim d As New addProject(AddressOf addProjectInvoke)
CreateNewSite.cbProject.Invoke(d, New Object() {s})
Else
CreateNewSite.cbProject.Items.Add(s)
End If
End Sub
possibly how i'm calling the delegate??
any help is appreciated
**** thanks to #jods here is the working code with one of the invoke methods****
starting thread in another modul
t1 = New Thread(New ParameterizedThreadStart(AddressOf GetNewClientData))
t1.Start(Me)
Code within the Modul
Function GetNewClientData(ByVal oldForm As CreateNewSite)
Try
Dim con As New SqlConnection
Dim myConString As String = My.Settings.ConString
Dim objcommand As SqlCommand = New SqlCommand
With objcommand
.Connection = con
Dim cmdText As String = "SELECT distinct Applicant,Client,Market,Project from AAClient order by Client"
.CommandText = cmdText
End With
con.ConnectionString = myConString
con.Open()
Using readerObj As SqlClient.SqlDataReader = objcommand.ExecuteReader
'This will loop through all returned records
While readerObj.Read
addApplicantInvoke(readerObj("Applicant").ToString, oldForm)
addClientInvoke(readerObj("Client").ToString)
addMarketInvoke(readerObj("Market").ToString)
addProjectInvoke(readerObj("Project").ToString)
End While
End Using
con.Close()
Catch ex As Exception
MsgBox(ex)
End Try
Return Nothing
End Function
Delegate Sub addApplicant(s As String, oldform As CreateNewSite)
Sub addApplicantInvoke(ByVal s As String, ByVal oldform As CreateNewSite)
If oldform.InvokeRequired Then
Dim d As New addApplicant(AddressOf addApplicantInvoke)
oldform.cbApplicant.Invoke(d, New Object() {s, oldform})
Else
oldform.cbApplicant.Items.Add(s)
End If
End Sub
The problem is CreateNewSite.cbProject. CreateNewSite is not your form instance. It's a nifty :p VB.NET feature called the default form instance:
VB has a concept of "Default Form Instances". For every Form in the application's namespace, there will be a default instance created in the My namespace under the Forms property.
You need to pass the correct form instance (i.e. 'Me' / 'this') to your background thread.