updating a database using a table adapter and data table - vb.net

I am currently working on a VB program connected to an Access database. I have fill a data table using a query and data adapter. At a later stage in the program, i want to go through and make permanent changes to the database using the adapter and table. I tried this:
For Each row As DataRow In db.DBDT.Rows
row("fldsentda") = "Y"
row("flddasenddate") = Date.Today
Next row
'db.DBDT.AcceptChanges()
db.DBDA.Update(db.DBDT)
*db is a class file, dbda is the data adapter, and dbdt is the data table
but i realized these changed are only effected the data table and not the actual database. How can I get it to where it will effect only the database rows that are inside of the data table filled using the query?
Update: I'm thinking my update function isn't written. I don't know if this should be a separate question or not, but how do I write the update function to only change fields in the database that has been changed on the data table??

Do not call
db.DBDT.AcceptChanges()
before
db.DBDA.Update(db.DBDT)
Doing so marks everything in the datatable as not changed. See here especially in remarks section.
Just call the update method and the acceptchanges should be called automatically for you.

Related

How to resolve issue with DataSet and Database connection in Visual Studio?

I am using VB.NET with an MS Access database. There are two tables with a relationship with each other.
I followed the following to make a database connection with the dataset and binding Source.
Under Data Source add new Data Source
Database as a data source type
Dataset as a database model >>
Chosen Data connection
Under the database object, I selected Table (s) which want for the purpose like customer table also clicked on views
Then finish.
Now at Data source, selected Dataset then Table of Customers and drag details and data grid view to the form and add buttons for adding, deleting updating the records.
Now run the application.
After running the application, But it's not viewing, adding, updating, and deleting records from/to the database.
Code for adding a record to the database
CustomersBindingSource.AddNew()
Code for updating a record to the database
CustomersBindingSource.EndEdit()
CustomersTableAdapter.Update(SrsbdbDataSet.Customers)
Code for deleting a record from the database
CustomersBindingSource.RemoveCurrent()
I also edited a connection string from the app.config file to check the connection string issue but not useful for the issue.
Please let me know where I'm doing wrong.
CustomersBindingSource.AddNew()
This doesn't add a record to the access database, it adds a record to the BindingSource's list, which (when EndEdit is called on the BindingSource) is pushed into the YourDataSetName.Customers DataTable as a new DataRow - if you were to look at all the rows in YourDataSetName.Customers you'd see that there are some (downloaded from the db probably, when you started the app) and they have a DataRowState of Unchanged, and then there is the new one you added, with a DataRowState of Added
Nothing has been saved to the DB yet. This data is only in the dataset's datatable, which is a client side representation of a database table. It is not a database table in and of itself. It can certainly have more or fewer columns and of different types, than the database table. It's just temporary storage for database data; you download some, add some, change some, delete some, maybe save it etc. The relevant DataRow tracks all these things you do to its data and notes whether it is Added/Modified/Deleted/Unchanged etc
The TableAdapter is the thing that pushes the data back and forth between the DataTable and the database
You call CustomersTableAdapter.Update() when you want to save the data to the DB. Naming it Update was a crap idea on Microsoft's behalf, because it leads people to think it only performs SQL UPDATE queries; if it had been called SaveChanges (and later it was; EF uses SaveChanges) it would be more clear.. You just have to remember that one - "Update means Save"
So you call Update(datatable or dataset here) and pass in your DataTable with all its modified/deleted/added rows. The TableAdapter scans the whole DataTable row by row looking at the DataRowState of each row. If it's Added, then the TableAdapter will call its built in INSERT SQL query to save the row. If it's Modified, SQL UPDATE is performed. Deleted state causes an SQL DELETE. A datarow knows the original data that was downloaded and the data as it is now; this is sometimes vital in working out if someone else saved this row in the time we had it, so we can avoid overwriting their changes with ours
At the end of this process, the data has been saved, the rowstates have all been set from whatever they were, to Unchanged (because the data in the db is now the same, the row data no longer needs saving).
Think of that part of the process as being like the little * that appears on a text editor tab, when you edit the file - a datarow in state Added/Modified/Deleted has unsaved changes that need to be saved. After saving, the state goes back to Unchanged. Did I mention that TableAdapter.Update should have been called Save?
All in, the process for saving would be to ask the editing control to EndEdit() then ask the relevant bindingsource to EndEdit - this ensures we have a datatable with all changes committed and ready to save, and then call the tableadapter.Update. Probably the control the user was typing in will commit its edits when it loses focus, as the user clicks the save button.. But calling endedit makes sure. If you're uncertain, create a new form, drop a DataGridView on it out of the Data Sources window and take a look how the Save button is wired up - from memory it does a Validate, couple of EndEdits and a UpdateAll (TableAdapterManager, manages TableAdapters, calls Update on them in the right order to make sure that parent rows save before child rows)
If you started making more modifications, the row states would change again but just as before, the thing that commits the changes to the DB is TableAdapter.Update() regardless what kind of change you made
The final thing to watch out for here is that Access is a file based database. Probably you have your project in e.g.:
C:\projects\accesswhatever\
And you had your access db on e.g. your desktop:
c:\users\you\desktop\access.mdb
When you connected the access db into things, VS presented a long and wordy dialog (that no-one reads ;) ) where it basically says "i'll put the db in your project, and I'll make it copy out to the bin folder when you build".
So you click OK without considering the ramifications of it and you build. Your disk now looks like:
C:\users\you\desktop\access.mdb 'call it DB X
C:\projects\accesswhatever\access.mdb 'call it DB Y
C:\projects\accesswhatever\bin\debug\access.mdb 'call it DB Z
Your running program will save data in the last one, DB Z. Every time you build (which might happen every time you click play, if you make code changes), visual studio will delete Z and copy Y to Z.
You're now really confused; your code says it's saving. You're looking in either DB X on your desktop, or DB Y in your project base, and wondering where the heck is this data?
It's in DB Z, in the bin\debug folder, next to your app.exe - just remember that every time you build, VS wipes your changed database and replaces it with a clean one from way back when. If you want to change this, click the DB in solution explorer and set "Copy To Output" from "Copy Always" to "Copy If Newer". Now it'll only copy whenever you make a schema change, so.. Add a new table and then VS will wipe your nicely curated test db with a new one.. But it's more like OK because the new empty DB at least has that extra table that your program will crash without :)
An alternative is to add the new record directly in DataGridView and use new OleDbDataAdapter for the connection.
Remove 'CustomersBindingSource.AddNew()', and edit record in DataGridView:
Code in 'Update Record' button.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim connstring = "your connection string"
Dim adapter As OleDbDataAdapter = New OleDbDataAdapter("select * from Customer", connstring)
Dim builder As OleDbCommandBuilder = New OleDbCommandBuilder(adapter)
builder.QuotePrefix = "["
builder.QuoteSuffix = "]"
adapter.Update(CustomerDataSet.Customer)
End Sub

Calling a subroutine as part of a SQL Transaction

My project is an orders application, where users can enter orders from different customers, using an UltraGrid to enter and edit data. When the Delete key is pressed on the keyboard, it prompts a MsgBox for the user to confirm they want to delete the selected row, and if so, it runs a DELETE query to delete that row from the database.
I also have a Save button to allow the user to save the order/changes made to the order. Ideally, I only want the row in the grid to delete temporarily as, if the user then doesn't save the order, the order line shouldn't be deleted permanently from the database.
If I put the DELETE query into a separate subroutine, can I then call this subroutine from my class that is saving it as part of the transaction?
This seems like it isn't going to work, as I'd not only need to call the query, but also somehow store the deleted row somewhere temporarily so that it knows which data to delete in the transaction, as well as a Boolean variable to tell it whether there is even any data to delete...
Are there any simpler ways of doing this? Would the above way even work?
Sorted it... Nothing actually complex required at all, just needed to change the code, as below:
Try
Dim Dc As New OleDbCommand
Dim rowcode As String = ""
rowcode = dr.Item("Product_Code").Value
Changed to:
Try
Dim Dc As New OleDbCommand
Dim rowcode As String = ""
rowcode = dr.Item("Product_Code", DataRowVersion.Original)
Simplest way of doing it, is keeping all of your changes in-memory (additions, modifications and deletions) and then synchronizing them to the database when you hit your Save button.
Possibly, you'll need an AJAX action to add the operation to the server-side and a client-side script to update the row on the grid.
Grid does not work with back-end database. It is only dealing with its local DataSource. So when you save the data back to your database check for deleted rows and preserve them.
If you want to update the local data source only on save button click you can set the grid's UpdateMode to OnUpdate. Then you will need to call grid's UpdateData method. Again, this will update your local data source. How and when the local data source will update the back-end database has nothing in common with the grid. So depending on the type of your local data source you will need to handle checking for deleted rows before you send updated data back to database.

How to address the TableAdapter of a Dataset (made by wizard)

I created a new datasource with 4 tables of a sql database. In my first form i created a to_table_1 _bounded gridview via drag and drop. This created table Adapter and table adapter manager for example, which a bound to the form. Now I have a module1 where I want to perform an Insert Statement from the Table2Tableadapter. But I dont know how i can talk to the adapter, if I not create a gridview anywhere. I want to talk to the Datset.xsd table2 Adapter. Ther I already set up the sql Statements and the Insert statement is nevertheless Standard implemented in the adapter.
I know this must be so easy for someone who worked more then one time with databases. Hope You can help!
Cheers Steven
If you want to do your data access in a module then don't create the table adapter in the form; create it in the module. The Data Source wizard generates classes and you use those classes the same way you would any other. Do you have a problem using a String in a module? Of course not., If you want a String then you create a String. The same goes for a table adapter.
When you drag items onto a form from the Data Sources window it will generate some items automatically. There's no requirement that you keep all those items. Just delete the table adapter and manager and any code that uses them, then write your own code to populate a DataTable in your module. You've still got the DataSet in the form so you simply pass that, or a specific DataTable it contains, to your module to populate it.
You could go further and remove the DataSet from the form too and then use the GetData method of your table adapter to create a new DataTable and return that to the form. It's up to you. Don't limit yourself by what's generated for you. That is there to help but it's not the only way to do things. If you want to create something then create it.

copy data from 1Db to another using Dataset

Sorry if my english is not so good, I will try to explain the problem.
I need to copy all data from a Table of a Acces mdb (connected with OleDB) to a Table of a MySql DB (connected with ODBC)
I made a working solution, but it's very slow, and so I want to try other solutions to check if they can give me more performance.
The solution connecting to the mdb, using a DataReader, then for each row in Datareader I make an INSERT into the Mysql Table ( before copy I truncate the table to get it empty)
The records are more than 10K and this operation is very slow, and do be onest I need to do the same thing on other 2 tables also very big as this one.
I cannot make a direct sql insert ( as INSERT INTO A in ..... SELECT * FROM B) because the 1 DB has a OleDB conn and the other has a ODBC conn.
So I thought to try to make this operation using TableAdapters and DataSet, but I'm not able to make it working.
The problem is that the Dataset's HasChanges is false
If you need some code I can post, but what I do is following:
Connection to MDb
Create OleDbTableAdapter
Create DataSet
Fill DataSet with TableAdapter
Connection to MySqlDB
Create ODBCTableAdapter
Using Update command of ODBCTableAdapter with the first Dataset.
But DS has no changes commited so he don't write anything to DB, so I thought to use another Dataset and copy data from DS1 to DS2 to add rows, see if has.changes was true and the making Update command of ODBCTableadapter using DS2.
I tryed to copy data between datasets:
ds2 = ds1.copy
I tried also to use dataset import function, looping DS1 datarows and Importing all rows from DS1 to DS2.
In both cases the rows are added to DS2, but still HasChanges is false, what can I do?
Just to clarify possible questions I didn't use DS.Acceptchanges, PrimaryKey is defined, The UpdateCommand is defined, DS has data (I populate 2 DataGrids to check it).
No errors given, just no data written on DB.
Any suggestion? Thanks in advice.
The only way you can speed up the process in a great deal would be to batch your SQL update commands. Otherwise each update request will be executed one at a time.
You might want to consider the MySQL LOAD DATA INFILE command to facilitate the rapid import of large amounts of data.
I wasn't able to locate a function comparable to MS SQL Server's SqlBulkCopy class, but if your MySQL library supports a similar function that may be of interest as well.
In general, the less network traffic you generate the faster you'll be able to perform large database inserts (although there can be other limiting factors after a point).

VB.NET Update Access Database with DataTable

I've been perusing some hep forums and some help books but cant seem to get my head wrapped around this. My task is to read data from two text files and then load that data into an existing MS Access 2007 database. So here is what i'm trying to do:
Read data from first text file and for every line of data add data to a DataTable using CarID as my unique field.
Read data from second text file and look for existing CarID in DataTable if exists update that row. If it doesnt exist add a new row.
once im done push the contents of the DataTable to the database.
What i have so far:
Dim sSQL As String = "SELECT * FROM tblCars"
Dim da As New OleDb.OleDbDataAdapter(sSQL, conn)
Dim ds As New DataSet
da.Fill(ds, "CarData")
Dim cb As New OleDb.OleDbCommandBuilder(da)
'loop read a line of text and parse it out. gets dd, dc, and carId
'create a new empty row
Dim dsNewRow As DataRow = ds.Tables("CarData").NewRow()
'update the new row with fresh data
dsNewRow.Item("DriveDate") = dd
dsNewRow.Item("DCode") = dc
dsNewRow.Item("CarNum") = carID
'about 15 more fields
'add the filled row to the DataSet table
ds.Tables("CarData").Rows.Add(dsNewRow)
'end loop
'update the database with the new rows
da.Update(ds, "CarData")
Questions:
In constructing my table i use "SELECT * FROM tblCars" but what if that table has millions of records already. Is that not a waste of resources? Should i be trying something different if i want to update with new records?
Once Im done with the first text file i then go to my next text file. Whats the best approach here: To First look for an existing record based on CarNum or to create a second table and then merge the two at the end?
Finally when the DataTable is done being populated and im pushing it to the database i want to make sure that if records already exist with three primary fields (DriveDate, DCode, and CarNum) that they get updated with new fields and if it doesn't exist then those records get appended. Is that possible with my process?
tia
AGP
Loading every text file into memory is the better performing option, and easier to code. This is of course fully dependent on how much memory you have available, and how big your text files are.
My approach would be to first load all of the data from the files into a DataTable. Then convert this table into XML. You can then pass the XML into a Stored Procedure. This stored procedure will convert the XML into either a Table Variable or Temporary table that you can run SQL queries off of.
From here it’s a simple case of doing a “Not Exists” query in your SP on tblCars with the data you’ve passed in, and inserting were applicable.
In my mind this is by far the best performing option, there’s no need for your application to pull any data out of SQL. SQL is optiomized for these kinds of things and will out perform a application hugely.
If you wanted to get really clever about it you could read each text file using a worker thread, and load them into SQL as soon as they’ve been read. It would save on memory usage and be very fast.