VB.net Disconnected Table - sql

I'm trying to load configuration settings into a set of DataTables and then disconnect from the database. I'm sure I am doing something wrong as the DataTables are empty afterwards. Here is the code:
From the top of the class:
'Set up Configuration DataTables
Dim dtDataAlerts As New DataTable
Running the Routine to get the data:
Public Sub ReadDataAlerts()
'Read the configuration for the Data Alerts from the RDB
dtDataAlerts = GetRecordSet("SELECT DataAlertGroups.DataAlertGroup_UID, DataAlertGroups.EmailGroup_UID, DataAlertGroups.EmailTemplate_UID, EmailTemplates.EmailTemplate_CustomSubject, EmailTemplates.EmailTemplate_CustomMessage FROM (DataAlertGroups INNER JOIN EmailGroups ON DataAlertGroups.EmailGroup_UID=EmailGroups.EmailGroup_UID) INNER JOIN EmailTemplates ON DataAlertGroups.EmailTemplate_UID=EmailTemplates.EmailTemplate_UID")
End Sub
The routine to get the DataTable
Function GetRecordSet(SQLString As String) As DataTable
'function used to run a query and return a disconnected DataTable
Try
'Create Dataset, Open Connection
Dim dsPWC As New DataSet()
Dim OleDbDataAdapter1 As System.Data.OleDb.OleDbDataAdapter = New System.Data.OleDb.OleDbDataAdapter(SQLString, sqlConn)
sqlConn.Open()
'Fill the Dataset with the PlantWatchConfiguration Table
OleDbDataAdapter1.Fill(dsPWC, "CollectorAlertGroups")
'Create Table from Dataset and iterate data
Dim DT As New DataTable
Dim DTClone As New DataTable
DT = dsPWC.Tables(0)
DTClone = DT.Clone
'Close Connection
sqlConn.Close()
Return DTClone
Catch ex As Exception
WriteToLog(ex.Message, "GetRecordSet")
End Try
End Function
I know the SQL is correct as I have already run it against the RDB and it produces results. I have a ton of logging to file in my code, so I am sure the database is connecting properly. (And other direct queries pull back data just fine)
But I am doing something wrong on the GetRecordSet function. I'm thinking that I am not properly cloning and disconnecting?
Anyway... I'm sure this is simple since I am not a coding expert :)
Thanks.

The error is exactly the Clone method. It clones the datatable structure, not the data.
DataTable.Clone: Clones the structure of the DataTable, including all DataTable schemas
and constraints.
If you want the data you need to use the Copy method.
DataTable.Copy: Copies both the structure and data for this DataTable.
However, as I have said in my comment, you don't need to waste the memory of your PC in this way.
ADO.NET is based on the disconnected model. The datatables present in the Dataset filled by the adapter are already disconnected from the database and you could return directly the table at index zero without any useless copy/clone.

Related

Filtering by ID column

I am trying to filter a table from Ms access DB in visual studio by ID (PID) column but I can't figure out how to convert int to string, or to make it work.
Any ideas please.
con.ConnectionString = dbProvider & dbSource
con.Open()
If PIDfindTextBox.Text = "" Then
MessageBox.Show("Please enter a Patient ID number")
Else
PatienrocordBindingSource.Filter = "((PID,'system.string') like '" & PIDfindTextBox.Text & "')"
If PatienrocordBindingSource.Count <> 0 Then
With dataGridView1.DataSource = PatienrocordBindingSource
End With
Else
MessageBox.Show("Searched Patient ID was not found")
PatienrocordBindingSource.Filter = Nothing
End If
End If
End Sub
Your code is very confused. It opens a database connection then sets a bindingsource filter, this isn't really how things go
A bindingsource is a device that sits between a model of data such as a datatable (which is not a table in a database) and a UI element such as a datagridview or other controls. The bindingsource can filter the data it finds in the model. None of this is anything to do with filling the model up with data in the first place, which often is done by pulling it out of a database
You can probably avoid interacting with a database connection entirely; if you use an OledbDataAdapter you can pull data out of your database and load it into a datatable in your program's memory
You thus have a choice of where to filter your data. You either load the entire database into your program's memory and then use bindingsource filter to show just the one if you want - all the data lives in the datatable and only some of it passes through the bindingsource ..
Or you only download some of the data out of the database and into the datatable, and then you don't need to filter it in the bindingsource
You can also take a hybrid approach of loading, say, 10 records into the datatable and then filtering In the bindingsource
Choose your scenario - most people opt for the middle one where you just download the data you need. There are plenty of examples out there but as a rough template, searching a database table by I'd and pulling the results would look like:
Dim da as New OleDbDataAdapter("SELECT * FROM Person WHERE Id =?", "connection string here")
da.SelectCommand.Parameters.AddWithValue("id", Convert.ToInt32(idTextbox.Text))
Dim dt as New DataTable
da.Fill(dt)
bindingsource.DataSource = dt
If you had 10 rows in your datatable and wanted to filter to just some one id
bindingsource.Filter = "[Id] = " & idFilterTextBox.Text
You don't need to use both of these approaches; use the one that is right for your context

Editing DataGridView data and updating changes directly into MySQL database

I want to save the changes that have made in the datagridview. I have code for adding the existing entries on the datagridview but I don't know how to update the changes made on datagridview
here is the code i am using to for adding the datagridview entries to the database
Public Sub ADD_DGV_CMD(SENDER As String)`
For Each row As DataGridViewRow In ALL_BILL_FRM.DataGridView1.Rows
Dim constring As String = "server=localhost; user=root; password=Masoom1; database=airtech_db; convert zero datetime=true;"
Using con As New MySqlConnection(constring)
Using cmd As New MySqlCommand("Insert into `all_bills` values(null,#Supplier_Name,#Bill_No,#Bill_Type,#Bill_Amount,#Bill_Date);", con)
cmd.Parameters.AddWithValue("#Supplier_Name", row.Cells(1).Value)
cmd.Parameters.AddWithValue("#Bill_No", row.Cells(2).Value)
cmd.Parameters.AddWithValue("#Bill_Type", row.Cells(3).Value)
cmd.Parameters.AddWithValue("#Bill_Amount", row.Cells(4).Value)
cmd.Parameters.AddWithValue("#Bill_Date", row.Cells(5).Value)
con.Open()
cmd.ExecuteNonQuery()
con.Close()
End Using
End Using
Next
MessageBox.Show("Records inserted.")
End Sub
You keep adding parameters in a loop. If your table contains more than one then all the parameters will be added again. They don't replace the existing ones, they supplement them, hence the "already added" error
Move your parameter adds to outside the loop. Chef get the way you work so that he parameter values are updated in the loop..
I would say "you should do:"
.Parameters("#Supplier_Name").Value = row.Cells(1).Value.ToString
..inside the loop, but what you should actually do is toss this lot out and take a look at using a strongly typed dataset, dapper or entity framework. If you're keen to carry on filling your button click handlers with SQL, dapper lets you do that but you might well get more out of something more high level
The short short version of using datasets is:
add a dataset to your project
open it, right click the surface and choose Add TableAdapter
connect to MySQL, ensure you have installed both the driver and the visual studio tools
enter SELECT * FROM all_bills as the query, finish the wizard
save the dataset
open the forms designer
open the data sources window (view menu, other windows)
drag the grid representing your table out of the data sources window and drop it on the form
run the project
Your grid will load and be able to to read and write the dB, not a single line of code written (by you; visual studio wrote a whole lot behind the scenes - you can see it in various .Designer.vb files)!
Public Class MAIN_FRM
Dim changes_counter As Integer, SEARCH_TYPE As String
Dim con As New MySqlConnection("server=localhost; user=root; password=Masoom1; database=airtech_db;")
Dim da As New MySqlDataAdapter("Select * from all_bills;", con)
Dim ds As New DataSet
Dim cmdBuilder As New MySqlCommandBuilder
Dim LOAD_CMD_CALL As Boolean
Private Sub UPDATE_BTN_Click(sender As Object, e As EventArgs) Handles UPDATE_BTN.Click
Try
cmdBuilder = New MySqlCommandBuilder(da)
ds = ds.GetChanges()
If ds IsNot Nothing Then
da.Update(ds)
MsgBox("Changes Done")
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
END SUB

Queries appear to work, but don't actually affect Access database

I've added an access db to my project as a datasource. So I get the automatically generated tableadapters class and therefore, access to the table adapter instance which includes the connection string. I'm using this to open a connection to my db so I can first, delete some records, and then replace them with new records.
The queries seem to work because the .executenonquery does return the rows affected. I even tried a delete * command to be sure. But, when I open the database everything is the same.
I had some ideas as to why. I thought the connection string returned by the tableadapter might be goofy because it contains a generic pointer to the project's data directory.
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\DupeMDB.mdb;Persist Security Info=True
I also thought maybe I had a problem with the Build Action or the Copy to Output Directory. I really don't understand the mechanics behind these two things. I think maybe the copy to output directory thing might be the culprit.
Here's me goal. I want to deploy this project to my secretary so she can use the program to deal with a duplicate record list etc. The data has to go with the program. I want to package this .mdf file with the deployment and get it back from her when she's done with it. I am so close to the end here (writing back to the table). Does anyone know why the table won't update?
Dim Connector As DupeTblTableAdapter = New DupeTblTableAdapter
Dim Conn As New System.Data.OleDb.OleDbConnection
Conn = Connector.Connection
Conn.ConnectionString = Connector.Connection.ConnectionString
MsgBox(Conn.ConnectionString)
Dim Comm As System.Data.OleDb.OleDbCommand
Conn.Open()
For Each DR In DeleteRecords
Comm = New System.Data.OleDb.OleDbCommand($"DELETE from DupeTbl where DupeTbl.CUST_NO={DR.ToString}", Conn) '
Dim aff As Integer = Comm.ExecuteNonQuery
'MsgBox(aff)
Comm = Nothing
Next
For Each RR In ReplaceRecords
Comm = New System.Data.OleDb.OleDbCommand($"INSERT INTO DupeTbl ( CUST_NO, PREDIR, POSTDIR, SUFFIX, CUSTSIZE, AddFlag, IgnoreRecord ) VALUES ({RR.Cust_No}, '{RR.PreDir}', '{RR.PostDir}', '{RR.Suffix}', {RR.Size}, {RR.AddFlag}, {RR.Ignore});", Conn)
Comm.ExecuteNonQuery()
Comm = Nothing
Next
Conn.Close()
The issue in such cases is usually the working database being overwritten on each build. When adding a local data file to your project, it is added as a source file in the project folder. By default, the Copy to Output Directory property is set to Copy Always. That means that every time you build your project, which will happen each time you make a code change and run the project by default, the source file will be copied over the top of the working database in the output folder, thus wiping out any changes you made while debugging. To prevent this, change that property to Copy if Newer, which means that the working database will only be overwritten if you make a change to the source database, e.g. modify the schema.
Dim Connector As DupeTblTableAdapter = New DupeTblTableAdapter
Dim Conn As New System.Data.OleDb.OleDbConnection
Conn = Connector.Connection
Conn.ConnectionString = Connector.Connection.ConnectionString
MsgBox(Conn.ConnectionString)
Dim Comm As System.Data.OleDb.OleDbCommand
Conn.Open()
For Each UR In UpdateRecords
Comm = New System.Data.OleDb.OleDbCommand($"UPDATE DupeTbl SET CUST_NO = <NewValue>, PREDIR = <NewValue, POSTDIR = <NewValue> etc. where DupeTbl.CUST_NO={DR.ToString}", Conn) '
Dim aff As Integer = Comm.ExecuteNonQuery
'MsgBox(aff)
Comm = Nothing
Next
Conn.Close()

selecting from one database and inserting row to new database

I'm looking for a pseudo code to write a VB.NET cursor to select columns from one table in one database and inserting it into another table in a different database (they are *not on the same server) using data adapter etc.
I just need something to refer to as I learn. Thanks
Dim selectStr As String = _
"select * from db1"
Dim insertStr As String = _
"insert into db2(col1)"
Try
da_adapter = New OleDb.OleDbDataAdapter(selectStr, connStr)
da2_adapter = New OleDb.OleDbDataAdapter(insertStr, connStr2)
da_adapter.SelectCommand.CommandTimeout = 720
da_adapter.Fill(ds)
da2_adapter.SelectCommand.CommandTimeout = 720
da2_adapter.Fill(ds)
Catch ex As Exception
End Try
In your original code, this part is wrong:
da2_adapter = New OleDb.OleDbDataAdapter(insertStr, connStr2)
When you create a data adapter that way, the SQL statement you provide becomes part of the SelectCommand. You want yours to be part of the InsertCommand, which you need to do yourself:
Dim cmd2 As New OleDb.OleDbCommand(insertStr, connStr2)
da2_adapter = New OleDb.OleDbDataAdapter
da2_adapter.InsertCommand = cmd2
Also, you need to set the AcceptChangesDuringFill property of the first data adapter to False. That way, instead of setting the RowState of all the rows to Unchanged after populating the DataTable, they will remain as Added and ready to be inserted.
You then need to call Update on the second data adapter to save the data rather than calling Fill, which retrieves data.

Multiple Database connections within 1 application - VB .NET

This may have been answered but my search hasn't found what I was looking for.
Basically, I am developing an application which allows the user to build a query at design time, i.e. for users with no prerequisite knowledge of SQL
The application thus far allows the user to select which table(s) from the database they wish to start querying (I won't go into the details of the rest for now)
My confusion is this; I already have the connection to the database in a subroutine which obtains the schema information and filters it to display only the available tables within the database, which then compiles the data into a listbox, here is that sub:
Public Sub getSchemaInfo()
Dim ds As New DataSet
Dim dt As New DataTable
Dim con As New OleDbConnection
Dim strDatabaseLocation As String = Application.StartupPath
Dim da As New OleDbDataAdapter
Dim i As Integer
'ds.Tables.Add(dt)
con.ConnectionString = "Provider=microsoft.jet.oledb.4.0; data source = " & strDatabaseLocation & _
"\EmployeeDepartment.mdb"
'clear listbox of any data first
frmAddTable.lbTables.Items.Clear()
'Try catch block used to handle connection errors gracefully
Try
con.Open()
'Accessing methods to obtain schema information
dt = con.GetOleDbSchemaTable(OleDb.OleDbSchemaGuid.Tables, New Object() _
{Nothing, Nothing, Nothing, "TABLE"})
'loop datatable to store schema information within it
For i = 0 To dt.Rows.Count - 1
'compile lbtables with a list of available tables from the database
frmAddTable.lbTables.Items.Add(dt.Rows(i)!TABLE_NAME.ToString())
Next
Catch ex As Exception
MessageBox.Show(ex.Message.ToString(), "Data Load Error", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation)
End Try
con.Close()
As you can see, at the very top is all the information regarding the connection to the database and loading of the information into the dataset.
My question is this; Whenever I need to gain access to the database and any information within it, will I have to perform all the connection process (oledbconnection, etc..)
or is there I way I can create a class for the connection functions and simply reference them whenever I need to connect?
For example, I now am in the process of creating another sub which gathers the columns, based on the tables chosen in the listbox, and displays it back onto the main form in the relevant checklistbox, again, connecting to the database, therefore would I need to perform all of the connection processes?
Any information would be very useful, thank you!
It is a standard approach to separate your DAL ( Data Access Logic ) from your Business Logic. I would definitely create a separate class for connecting to the database and executing the queries that would bring back the results that you can then either Bind to a control or iterate over inside a loop.
You might even want to look into using EF ( Entity Framework ) or my favorite LINQ to SQL to help in following a standard Design Pattern. By using a framework like EF or L2S you can leverage their ability to cache objects and return back strongly typed objects versus loosely typed. Strongly typed objects give you intelisense and are less prone to common mistakes like misspelling a field from a DataTable.