How do I create a dataset in VB.NET from this code? - vb.net

I currently have the following code in my project which populates a DataGridView object with the results of an SQL query.
Sub PerformQuery(ByVal SQLText As String)
Dim DbConnection As New OleDb.OleDbConnection(createConnectionString)
Dim SQLQuery As String = SQLText
Dim Adapter As New OleDb.OleDbDataAdapter(SQLQuery, DbConnection)
Try
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Elsewhere in my project I can create a DataSet object using the code
Dim ds As New DataSet
And then extract data from it using code like:
MaxRows = ds.Tables("Dataset_Users").Rows.Count
Rather than populating a DataGridView, how can I use the PerformQuery code to create a dataset?
Thank you in advance for your help.

I think you are after the following:
Try
Dim ds As New DataSet
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
ds.Table.Add(Table)
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
Or as in your example you was after the Number of rows in the dataset you can do the same with the DataTable, For Example:
Try
Dim MaxRows As Integer
Using Table As New DataTable
Adapter.Fill(Table)
Table.Locale = Globalization.CultureInfo.InvariantCulture
DbConnection.Close()
DataGridView1.DataSource = Table
'' Getting the number of rows in the DataTable
MaxRows = Table.Rows.Count
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try

Think in a more functional style. Return the table instead of setting to grid. While we're here, let's update the method so you don't have to write queries anymore that leave you wide open to sql injection attacks:
Function PerformQuery(ByVal SQLText As String, ByVal ParamArray Parameters() As OleDbParameter) As DataTable
Dim result As New DataTable()
Using cn As New OleDb.OleDbConnection(createConnectionString), _
cmd As New OleDb.OleDbCommand(SQLText, cn), _
Adapter As New OleDb.OleDbDataAdapter(cmd, cn)
If Parameters IsNot Nothing AndAlso Parameters.Length > 0 Then
cmd.Parameters.AddRange(Parameters)
End If
Adapter.Fill(result)
End Using
Return Result
End Function
Note that I also removed the error handling and locale code. You still need to do that stuff, but when you want to just return a datatable instead of interact directly with the user interface in a method you have effectively moved your code to a lower level of abstraction. When you do that, you probably don't want to deal with the error handling at this lower level any more; instead let the exceptions bubble up where you can handle them closer to the user interface.
Now you call the updated method like this:
Dim sql As String = "SELECT * FROM Customers WHERE CustomerID = ?"
Dim CustID As New OleDb.OleDbParameter("CustomerId", OleDbType.Integer)
CustID.Value = 123456
Try
DataGridView1.DataSource = PerformQuery(sql, CustID)
Catch Ex As Excpetion
MsgBox(Ex.Message)
End Try

Related

VB.NET How to correctly loop through a result set

I have looked at many different code snippets on this site looking that would show me how to do something that should be fairly simple once I have the knowledge.
I want to query a database table for an array of values and then populate a combobox with those results.
Here is what I have so far:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Form1.machineName.DisplayMember = "Text"
Dim tb As New DataTable
tb.Columns.Add("Text", GetType(String))
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
For Each cmd As String In cmd
'I want to add each value found in the database to "tb.Rows.Add"
'tb.Rows.Add(???)
Next
Form1.machineName.DataSource = tb
cn.Open()
cmd.ExecuteNonQuery()
End Using
cn.Close()
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I proceeded much like you did. I used the Load method of the DataTable. It is not necessary to set the column name and type. The name of the column is taken from the Select statement and the datatype is inferred by ADO.net from the first few records.
Luckily a DataTable can be an Enumerable using the .AsEnumnerable method. Then we can use Linq to get all the values from the MachineName column. Calling .ToArray causes the Linq to execute. If you hold your cursor over names on this line you will see that the datatype is String(). Just what we need to fill a combo box.
Code for a class called DataAccess
Private ConnectionString As String = "Your Connection String"
Public Function GetMachineNames() As String()
Dim tb As New DataTable
Dim SQL As String = "SELECT MachineName from machine;"
Using cn As New MySqlConnection(ConnectionString)
Using cmd As New MySqlCommand(SQL, cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
Dim names = dt.AsEnumerable().Select(Function(x) x.Field(Of String)("MachineName")).ToArray()
Return names
End Function
In the form load you combo box like this.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim DatAcc As New DataAccess()
Dim arr = DatAcc.GetMachineNames()
machineName.DataSource = arr
End Sub
If you just want the MachineName to be displayed in the ComboBox, then just use that as the DisplayMember; don't bother creating another column called Text.
Public Sub getMachines()
Try
Dim cmd As String = "SELECT MachineName from machine"
Dim ds As New DataSet()
Using con As New MySqlConnection(ConnectionString)
Using da As New MySqlDataAdapter(cmd, con)
da.Fill(ds)
With Form1.machineName
.DisplayMember = "MachineName"
.ValueMember = "MachineName"
.DataSource = ds
End With
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
I'll show a few examples, including using parameters, since that is important.
First up, a quick translation to run the existing query and loop through the results:
Public Sub getMachines()
Try
Dim SQL As String = "SELECT MachineName from machine"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Form1.machineName.Items.Add(rdr("MachineName"))
End While
End Using
End Using
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
But better practice for a method like this is to isolate data access for the UI. This method should return results to the caller, which can decide what do with them. So I'll show two methods: one to get the data, and the other to loop through it and set up the combobox:
Private Function GetMachines() As DataTable
'No try/catch needed here. Handle it in the UI level, instead
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
For Each item As DataRow in getMachines().Rows
Form1.machineName.Items.Add(item("MachineName"))
Next
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
Or, we can use DataBinding:
Private Function GetMachines() As DataTable
Dim SQL As String = "SELECT MachineName from machine"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
da.Fill(result)
End Using
Return result
End Function
Public Sub LoadMachines()
Try
Form1.machineName.DisplayMember = "FirstName";
Form1.machineName.ValueMember = "City"
Form1.machineName.DataSource = GetMachines()
Catch ex As MySqlException
MsgBox(ex.Message)
End Try
End Sub
If you ever want to use a filter, you might do this (notice the overloading):
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Dim result As New DataTable
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn),
da As New MySqlDataAdapter(cmd)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
da.Fill(result)
End Using
Return result
End Function
Private Function GetMachines(ByVal machineFilter As String) As DataTable
Return GetMachines("%")
End Function
Query parameters like that are very important, and if you were doing string concatenation to accomplish this kind of thing on your old platform, you were doing very bad things there, too.
Finally, let's get fancy. A lot of the time, you really don't want to load an entire result set into RAM, as is done with a DataTable. That can be bad. Instead, you'd like be able to stream results into memory and only work with one at a time, minimizing RAM use. In these cases, you get to play with a DataReader... but returning a DataReader object from within a Using block (which is important) doesn't work that well. To get around this, we can use functional programming concepts and advanced language features:
Private Iterator Function GetMachines(ByVal machineFilter As String) As IEnumerable(Of String)
Dim SQL As String = "SELECT MachineName from machine WHERE MachineName LIKE #Filter"
Using cn As New MySqlConnection(ConnectionString), _
cmd As New MySqlCommand(SQL, cn)
'Match the MySqlDbType to your actual database column type and length
cmd.Parameters.Add("#Filter", MySqlDbType.VarString, 30).Value = machineFilter
cn.Open()
Using rdr As MySqlDatareader = cmd.ExecuteReader
While rdr.Read()
Dim result As String = rdr("MachineName")
Yield Return result
End While
End Using
End Using
Return result
End Function
Private Function GetMachines() As IEnumerable(Of String)
Return GetMachines("%")
End Function

VB.Net Parameter count mismatch

I'm writing a function that convert an Array List into a Data Table, and I'm getting an error "Parameter count mismatch".
I've been looking and googling the issue but couldn't found anything that could help me resolve my issue.
```
'Function that convert ArrayList to DataTable
Public Function ConvertArrayListToDataTable(ByVal MyAList As ArrayList) As DataTable
Dim dt As New DataTable()
For i As Integer = 0 To MyAList.Count - 1
'create a Generic Object
Dim values As Object = MyAList.Item(i)
Dim properties() As PropertyInfo =
values.GetType().GetProperties()
'Loop through each property, and add it as a column to the datatable
For Each prop As PropertyInfo In properties
Try
Dim dc As DataColumn = New DataColumn(prop.Name)
'Add the column definition to the datatable
dc.DataType = prop.PropertyType
dt.Columns.Add(dc)
Catch ex As Exception
MessageBox.Show(" Can't add column" & ex.Message)
End Try
Next
'for each object in the list, loop through and add
'the data to the database
For Each o As Object In MyAList
Try
'create new row
Dim row As DataRow = dt.NewRow()
Dim pf() As PropertyInfo = o.GetType().GetProperties()
For Each item As PropertyInfo In pf
row(item.Name) = item.GetValue(o, Nothing)
Next
dt.Rows.Add(row)
Catch ex As Exception
MessageBox.Show(" Conversionerror" & ex.Message)
End Try
Next
Next
Return dt
End Function
```
I see the code get the Table and the column count as well, but cannot write value. it failed on getvalue.
The function suppose return a datatable containing retrieved data and populate datagrid
For Each prop As PropertyInfo In properties
Try
Dim dc As DataColumn = New DataColumn(prop.Name)
dc.DataType = prop.PropertyType
dt.Columns.Add(dc)
Catch ex As Exception
MessageBox.Show(" Can't add column" & ex.Message)
End Try
Next
If you are filling a DataTable from a database just let the DataTable figure out the datatype. ArrayList is not used in new code. You can make a class or structure and then use List(Of T) or if absolutely necessary List(Of Object). Test just the plain old DataTable.
Private Sub FillDataGridView()
Dim dt = New DataTable()
Using cn As New SqlConnection(My.Settings.CoffeeConnection)
Using cmd As New SqlCommand("Select * From Coffees;", cn)
cn.Open()
dt.Load(cmd.ExecuteReader)
End Using
End Using
For Each col As DataColumn In dt.Columns
Debug.Print(col.DataType.Name)
Next
DataGridView1.DataSource = dt
End Sub
Thank you Mary. It was a great experience. I was challenging myself to see how I can fill the Datagrid from Arraylist to Datatable. I finally follow your advice and review my Function as below:
Public Function FillDataGridView(SQLStatement As String) As DataTable
'create an DataTable to hold the results
Dim dt As New DataTable()
Try
Using con As New SqlConnection(ConnectionString)
con.Open()
Using cmd As New SqlCommand(SQLStatement, con)
'Get the reader
Using Reader = cmd.ExecuteReader()
dt.Load(Reader)
End Using
End Using
End Using
For Each col As DataColumn In dt.Columns
Debug.Print(col.DataType.Name)
Next
Catch ex As Exception
Console.WriteLine("SQL retrieve row:" & ex.Message & Err.Number)
Finally
Call CloseConn()
End Try
Return dt
End Function

Displaying SQLite Data In DataGridView VB.net

I am using sqlite database and using vb.net i am trying to print some rows in datagridview. I use the following code:
Public Sub LoadUsername()
Dim ConnectionString As String = "Data Source=info.sqlite"
Dim nSQL As String = "SELECT Name From employee"
Dim dt As DataTable = Nothing
Dim ds As New DataSet
Try
Using con As New SQLiteConnection(ConnectionString)
Using cmd As New SQLiteCommand(nSQL, con)
con.Open()
Using da As New SQLiteDataAdapter(cmd)
Console.WriteLine(da)
da.Fill(ds)
dt = ds.Tables(0)
End Using
End Using
End Using
ListBox1.ValueMember = "Name"
ListBox1.DisplayMember = "FullName"
ListBox1.DataSource = dt
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
but when i execute or compile this program, it shows me System.Data.DataRowView in the field where name should be. please check the image. Thanks!
There is no FullName column in your datatable yet that's what you're trying to bind the display to. Change the DisplayMember to Name as it should work correctly.
System.Data.DataRow is just the default ToString implementation as it cant find your fullname property.

SQLXML Import/Export

I have a SQL DB from which I export data as XML using VB.Net code. The code is relatively simple, works quickly, and formats the XML beautifully. The code is:
Dim connetionString As String
Dim connection As SqlConnection
Dim adapter As SqlDataAdapter
Dim ds As New DataSet
Dim sql As String
connetionString = "**connectionstring**"
connection = New SqlConnection(connetionString)
sql = "select * from costdata"
Try
connection.Open()
adapter = New SqlDataAdapter(sql, connection)
adapter.Fill(ds)
connection.Close()
ds.WriteXml("**PATH**")
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
The problem I'm having is loading this data back in. It seems like it should be as easy as the above, but I can't seem to get a simple way to do it.
It's my understanding that I can use an XMLReader coupled with ADO.NET, but in that case I need to define the columns for the DataTable to insert the XML Data into before I import it all into the DB.
Is there any way to keep from having to hard-code column values in the DataTable, and have the exported XML data import in similar fashion to the above?
Though it's not automated by column name, I decided that hardcoding the mappings wasn't too big a hassle. I'm all ears for an automated way, however. My solution:
Dim connectionString As String = "Data Source=(localdb)\v11.0;Initial Catalog=localACETest;Integrated Security=True"
Try
Using sqlconn As New SqlConnection(connectionString)
Dim ds As New DataSet()
Dim sourcedata As New DataTable()
ds.ReadXml("C:\Users\coopere.COOPERE-PC\Desktop\Test.xml")
sourcedata = ds.Tables(0)
sqlconn.Open()
Using bulkcopy As New SqlBulkCopy(sqlconn)
bulkcopy.DestinationTableName = "ScheduleData"
bulkcopy.ColumnMappings.Add("Id", "Id")
bulkcopy.ColumnMappings.Add("Period", "Period")
...
bulkcopy.WriteToServer(sourcedata)
End Using
sqlconn.Close()
End Using
MsgBox("Done")
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Here is a way to automate column mappings ... it assumes the table exists with the same structure in the target database. Cheers :-)
Public Shared Function BulkCopyXML( _
path_ As String, _
connection_string_ As String, _
messages_ As List(Of String), _
exceptions_ As List(Of Exception) _
) As Boolean
Dim result_ As Boolean = False
Try
Dim dataset_ As New DataSet()
dataset_.ReadXml(path_)
Dim datatable_ As DataTable = Nothing
Using connection_ As SqlClient.SqlConnection = New SqlClient.SqlConnection(connection_string_)
connection_.Open()
Using bulkCopy_ As SqlClient.SqlBulkCopy = New SqlClient.SqlBulkCopy(connection_)
For Each datatable_ In dataset_.Tables()
messages_.Add(datatable_.TableName)
bulkCopy_.DestinationTableName = datatable_.TableName
bulkCopy_.ColumnMappings.Clear()
For Each dataColumn_ As DataColumn In datatable_.Columns
bulkCopy_.ColumnMappings.Add(dataColumn_.ColumnName, dataColumn_.ColumnName)
Next
bulkCopy_.WriteToServer(datatable_)
Next
End Using
End Using
result_ = True
Catch exception_ As Exception
If exceptions_ Is Nothing Then
Throw exception_
Else
exceptions_.Add(exception_)
End If
Finally
End Try
Return result_
End Function

Better way to print out rows from a datatable in vb.net

I am new to vb.net and I am trying to query a database and print out the records in the row to the console window. I got it to work, but I have a feeling that there is a more concise way to do this. One thing that I am sure is wrong is that I had to convert the dataset to a datatable to be able to retrieve the values. Is that correct? Could you take a look at the code below (especially the for loop) and let me know what I can improve upon?
Thanks!
Module Module1
Sub Main()
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Dim conn As New SqlCeConnection(constring)
Dim cmd As New SqlCeCommand("SELECT * FROM ACCOUNT")
Dim adapter As New SqlCeDataAdapter
Dim ds As New DataSet()
Try
conn.Open()
cmd.Connection = conn
adapter.SelectCommand = cmd
adapter.Fill(ds, "testds")
cmd.Dispose()
adapter.Dispose()
conn.Close()
Dim dt As DataTable = ds.Tables.Item("testds")
Dim row As DataRow
Dim count As Integer = dt.Columns.Count()
For Each row In dt.Rows
Dim i As Integer = 0
While i <= count - 1
Console.Write(row(i))
i += 1
End While
Console.WriteLine(Environment.NewLine())
Next
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
End Module
Here is how I would rewrite this for a few reasons:
1) You should always use Using statements with disposable objects to ensure they are correctly cleaned up. You had a good start with the dispose commands, but this way is safer.
2) It is more efficient to use ExecuteReader than loading everything into a dataset.
3) Your try/catch statement should include object creation as well as execution.
Finally, in response to your question about datasets and datatables, that code was absolutely correct: a dataset consists of zero or more datatables, so you were just extracting the existing datatable from the dataset.
Try
Dim constring As String = "Data Source=C:\Users\test\Desktop\MyDatabase1.sdf"
Using conn As New SqlCeConnection(constring)
conn.Open()
Using cmd As New SqlCeCommand("SELECT * FROM ACCOUNT", conn)
Dim reader As SqlCeDataReader
reader = cmd.ExecuteReader()
Do While reader.Read
For i As Integer = 0 To reader.FieldCount - 1
Console.Write(reader.GetString(i))
Next
Console.WriteLine(Environment.NewLine())
Loop
End Using
End Using
Catch ex As Exception
Console.WriteLine("There was an error")
Console.WriteLine(ex)
End Try
Console.ReadLine()
End Sub
One last note: since you are just printing to the console, it doesn't matter as much, but whenever you deal with a lot of strings, especially those that are to be concatenated, you should always consider using System.Text.StringBuilder.
Here is an example rewrite of the loop that prints to the console using stringbuilder (builds the string in memory, then dumps it to the console; I have also added the field name for good measure):
Dim sbOutput As New System.Text.StringBuilder(500)
For i As Integer = 0 To reader.FieldCount - 1
If sbOutput.Length <> 0 Then
sbOutput.Append("; ")
End If
sbOutput.Append(reader.GetName(i)).Append("=").Append(reader.GetString(i))
Next
sbOutput.AppendLine()
Console.Write(sbOutput.ToString)