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

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()
....

Related

How could I go about running access querys on a seperate thread?

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.

Do I need to dispose an instance of my custom class to free up memory? Then how? VB.NET

I just want to save time and codes to create a custom class in executing my SQL queries so I created just like this:
Imports ADODB
Public Class MySQLConnection
Private SQLConnection As ADODB.Connection
Private SQLConnectionString As String = "Provider=SQLOLEDB;Data Source=111.111.10.201;Initial Catalog=dbSample;User ID=User;password=123456;"
Private SQLRecordSet As ADODB.Recordset
Public Recordset As ADODB.Recordset
Public Message As String
Public Function ExecuteSQLQuery(vQuery As String) As Boolean
Try
SQLConnection.Open(SQLConnectionString)
SQLRecordSet.CursorLocation = ADODB.CursorLocationEnum.adUseClient
SQLRecordSet.CursorType = ADODB.CursorTypeEnum.adOpenStatic
SQLRecordSet.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
SQLRecordSet.Open(vQuery, SQLConnection)
Recordset = SQLRecordSet 'passing the content of recordset to a public recordset for later use in my main program.
SQLRecordSet.Close()
SQLConnection.Close()
Message = "Query executed successfully."
Return True
Catch ex As Exception
Message = ex.Message
Return False
End Try
End Function
End Class
QUESTION #1
But since I will be creating multiple instance of this class throughout my program, do I need to somehow dispose the instance to free up the memory immediately after use?
Whenever I need to execute my query, I use my code below in my main program:
Private Sub COnnectToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles COnnectToolStripMenuItem.Click
Dim DB_CONN As New MySQLConnection
If DB_CONN.ExecuteSQLQuery("SELECT * FROM tbl_Stores") Then
If DB_CONN.Recordset.RecordCount <> 0 Then
DB_CONN.Recordset.MoveFirst()
Do While Not DB_CONN.Recordset.EOF
'Read each record here
DB_CONN.Recordset.MoveNext()
Loop
End If
DB_CONN.Recordset.Close()
'==============================
'This is where I think I should dispose my class instance [DB_CON].
MsgBox("MESSAGE: " & DB_CONN.Message)
Else
MsgBox("ERROR: " & DB_CONN.Message)
End If
End Sub
QUESTION #2:
How do I dispose the instance of my class after use?
I just want to know this so I can clean up my previous programs.
All I find in Google is for C++ so I'm not sure if it works for VB.Net
Please help! :(
If I were you I'd look at making the ExecuteSQLQuery completely self contained such that it takes an Action(Of ADODB.Recordset). Then it can clean up after itself immediately after it has executed.
I'd initially write it like this:
Public Module MySQLConnection
Private SQLConnectionString As String = "Provider=SQLOLEDB;Data Source=111.111.10.201;Initial Catalog=dbSample;User ID=User;password=123456;"
Public Function ExecuteSQLQuery(vQuery As String, process As Action(Of ADODB.Recordset), message As Action(Of String)) As Boolean
Try
Dim conn As ADODB.Connection = New ADODB.Connection()
conn.Open(SQLConnectionString)
Dim rs As ADODB.Recordset = New ADODB.Recordset()
rs.CursorLocation = ADODB.CursorLocationEnum.adUseClient
rs.CursorType = ADODB.CursorTypeEnum.adOpenStatic
rs.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
rs.Open(vQuery, conn)
process?(rs)
rs.Close()
conn.Close()
message?("Query executed successfully.")
Return True
Catch ex As Exception
message?(ex.Message)
Return False
End Try
End Function
End Module
Now you'd use it like this:
Private Sub COnnectToolStripMenuItem_Click2(sender As Object, e As EventArgs) Handles COnnectToolStripMenuItem.Click
Dim success As Boolean = MySQLConnection.ExecuteSQLQuery("SELECT * FROM tbl_Stores",
Sub(recordset)
If recordset.RecordCount <> 0 Then
recordset.MoveFirst()
Do While recordset.EOF
'Read each record here
recordset.MoveNext()
Loop
End If
End Sub, AddressOf MsgBox)
End Sub
Or even better - make the method return an enumerable of some value:
Public Module MySQLConnection
Private SQLConnectionString As String = "Provider=SQLOLEDB;Data Source=111.111.10.201;Initial Catalog=dbSample;User ID=User;password=123456;"
Public Iterator Function ExecuteSQLQuery2(Of T)(vQuery As String, process As Func(Of ADODB.Recordset, T)) As IEnumerable(Of T)
Dim conn As ADODB.Connection = New ADODB.Connection()
conn.Open(SQLConnectionString)
Dim rs As ADODB.Recordset = New ADODB.Recordset()
rs.CursorLocation = ADODB.CursorLocationEnum.adUseClient
rs.CursorType = ADODB.CursorTypeEnum.adOpenStatic
rs.LockType = ADODB.LockTypeEnum.adLockBatchOptimistic
rs.Open(vQuery, conn)
If rs.RecordCount <> 0 Then
rs.MoveFirst()
Do While rs.EOF
Yield process(rs)
rs.MoveNext()
Loop
End If
rs.Close()
conn.Close()
End Function
End Module
Then you can do this:
Private Sub COnnectToolStripMenuItem_Click2(sender As Object, e As EventArgs) Handles COnnectToolStripMenuItem.Click
Dim values As IEnumerable(Of Integer) = MySQLConnection.ExecuteSQLQuery2(Of Integer)("SELECT * FROM tbl_Stores", Function(recordset) CType(recordset.Fields("Value").Value, Integer))
End Sub

How can I refresh DataGridView?

I'm trying to refresh datagridview after adding multiple records which were added programmatically, using bindingsource and datasource. My code:
Private Async Function RefreshData() As Task
Await Task.Delay(15000)
bs.EndEdit()
daProducts.Update(dtProducts)
DataGridView1.DataSource = Nothing
DataGridView1.DataSource = bs 'Insert your DataSource here
bs.ResetBindings(False)
...
When I call this function on form load event, it can't display any records.
Another thing is I get error if I don't use async function (because of setting DefaultCellStyle.Alignment).
My formload event: http://sudrap.org/paste/text/554747/
This might be helpful for you:
Private Sub updatedgv()
Dim conn As New MySqlConnection(My.Settings.myConn)
Dim da As New MySqlDataAdapter
Dim ds As New DataSet
Dim str1 As String = "select * from tableName"
da.SelectCommand = New MySqlCommand(str1, conn)
da.Fill(ds)
conn.Close()
ProductDataGridView.DataSource = ds.Tables(0)
End Sub
the explanation can be found in the link
http://www.codeproject.com/Questions/372731/how-to-refresh-datagridview-in-vb-net

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.

VB.NET - Closing Connection object and command object

Please have a look at the code below:
Public Class Form1
Private _ConString As String
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim objDR As SqlDataReader
Dim objCommand As SqlCommand
Dim objCon As SqlConnection
Dim id As Integer
Try
_ConString = ConfigurationManager.ConnectionStrings("TestConnection").ToString
objCon = New SqlConnection(_ConString)
objCommand = New SqlCommand("SELECT * FROM Person")
objCommand.Connection = objCon
objCon.Open()
objDR = objCommand.ExecuteReader(ConnectionState.Closed)
Do While objDR.Read
id = objDR("URN")
Loop
objDR.Close() 'line 16
Catch ex As Exception
throw
Finally
End Try
End Sub
End Class
The connection object is closed on line 16. Please have a look at the code below:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim objDR As SqlDataReader
Dim objCommand As SqlCommand
Dim objCon As SqlConnection
Dim id As Integer
Try
_ConString = ConfigurationManager.ConnectionStrings("TestConnection").ToString
objCon = New SqlConnection(_ConString)
objCommand = New SqlCommand("SELECT * FROM Person")
objCommand.Connection = objCon
objCon.Open()
Using objCon
Using objCommand
objDR = objCommand.ExecuteReader()
Do While objDR.Read
id = objDR("URN")
Loop
End Using
End Using
objDR.Close() 'line 16
Catch ex As Exception
throw
Finally
End Try
End Sub
I notice in both cases that the connection object and command object both still have state after they are closed (either by closing the data reader as per code sample 1 or moving outside the Using statement as per code sample 2). Could this be a source of a memory leak?
You should be doing something like this...
Using Conn as new SqlConnection(_ConString)
Dim cmd as New SqlCommand(Conn, "Select * FROM Person");
id = Convert.ToInt32(cmd.ExecuteScalar())
End Using
Wrap this in a try if you must, but only if you are going to handle it in some way. (A simple "Throw" is not handling anything.)
You are also using a DataReader (iterative, forward only) data access component to get a single value. You may want to use an ExecuteScalar() to simplify that code.
Aside from that, do not confuse "Disposed" as being the same as "Closed". The .NET framework manages garbage collection for you. If you want to dispose the connection and command objects, call .Dispose() on them (but the Using will take care of this for you!!!)
re: Could this be a source of a memory leak?
no, it shouldn't be.
also, this:
objDR = objCommand.ExecuteReader()
Do While objDR.Read
id = objDR("URN")
Loop
looks bad because it iterates through all the rows and overwriting the id values. The last row set ends up with the value in ID. You're taking one result from a set of more than one rows. If you just want the max(URN) from Person, you can write your command to return that result.