Saving DataTable to MS Access Table - vb.net

Background information:
I have a DataGridView, I can load a table from MS Access into it and/or also add data. It is not databound (and I don't want t).
What I did previously was to delete the data from MS Access table before saving new data. But I would like to do it the proper way (+ there will be less chance of losing data).
Issue:
When I do
Adapter.Update(DataTable) 'OleDb.OleDbDataAdapter
the data simply gets added (inserted) at the end, like when you add rows to datagridview.
What I want to do is overwrite the data in MS Access table, so that it looks identically to newly saved data (rows that can be updated get updated, new ones added, removed ones deleted)
In other words, if I'm saving 6 rows, I want to see 6 rows in database, not more not less.
I also tried loading the data, then changing it, and saving it back, but the result is the same. Eg if I load 5 rows, and save 5 rows, I end up with 10 rows in it after that save.
Adapter = New OleDb.OleDbDataAdapter("SELECT * FROM " & DB_TableName, DB_Connection)
Dim TempDataTable As New DataTable
BotDB_Adapter.Fill(TempDataTable )
'edit data here
Adapter.Update(TempDataTable)
TLDR : How do I save a DataTable to MS Access table so that it overwrites data already in it.

It was due to row state being "added", the only way to fix it is altering individual row states to proper states.
I do not see other way to solve this so I'll close it as there aren't any better answers.

Related

vb.net dataview grid won't add record and doesn't update after data is modified independently

I have a dataview grid bound to a datasource at run time. The datasource is filled from an access database via a DataAdapter. The data fills and displays correctly, and updates to existing rows seem to work OK but I have two problems:
When I type something in a new row and then press return or switch to a different row, I want the DataAdapter to add that row then and there to the database so I can retrieve the Autonumber index of the new record from Access and use that to add an associated record in a different table (Entries, a many to many linking table). This isn't happening. In the RowLeave event I have adapter.Update(dsSentences) and then I check for the new row, but the RowCount doesn't reflect its presence even though the newly added data is visible in the grid, and the adapter.Update doesn't seem to have triggered the Insert query that I specified in the DataAdapter. So nothing is added.
(edit: OK, so the new row has not yet been added when this event is fired. Which event should I then use to commit the data and retrieve the Autonumber primary key for my new record? I've tried UserAddedRow but that one fires before you've entered any data into the new row.)
THe second problem is that I need to update the data independently and then have the grid reflect those changes. How do I do that? Is there some call that will force the grid to get the updated data from the DataAdapter via the Dataset? Any help would be much appreciated. I'm almost ready to dtop the whole idea of binding data and do it all through code, Data binfing is supposed to save time but I'm finding it labyrinthine and unpredictable.
FWIW here's the query I'm using to fill the grid:
PARAMETERS nIdCollection Long;
SELECT tblSentences.IdSentence, tblSentences.SentenceText, tblSentences.SentenceParsed, Not IsNull([tblSentences]![SentenceParsed]) AS HasParsed, Entries.IdEntry
FROM tblSentences INNER JOIN Entries ON tblSentences.IdSentence = Entries.IdSentence
WHERE (((Entries.IdCollection)=[nIdCollection]))
ORDER BY Entries.SortValue;
As you can see, it requires a record in Entries. After I've entered a new record in tblSentences, before there are any entries the IdEntry will be null assuming it shows up at all. That's why I need to intercept directly after the Insert, add the record to Entries and requery to keep everything in order. You could do it all in an SQL stored procedure but I have to use Access.
Edit: After a lot of googling I've come to the conclusion that what I'm trying to do = add a record to a table through an additional INSERT query apart from the one handled by the DataAdapter, every time a new row is added - simply can't be done if you are using data binding. I am going to have to delete all my code and start from scratch populating the grid through code (unbound). I think it's the only way to do what I want. I will leave this here as a warning to anyone else not to make my mistake of trying to use Data binding when your data is coming from more than one table. Bad mistake.

VB.Net dataset rows count always returning 0

I created a table adapter for a stored procedure I have, which returns a row from a table. After adding it to the dataset, I try to loop through the row count of this table to retrieve the data but it keeps returning no rows. If I try to preview the data in the dataset designer, i get the row normally but when i try it in code i get nothing
For intI As Integer = 0 To Me.Ds1.SP_Get_Data_Communication_Parameters.Rows.Count - 1
Dim IP As String = Ds1.SP_Get_Data_Communication_Parameters.Rows(intI)("Remote_IP_address")
Next
A tableadapter is a device that moves data in either direction between a database and a datatable.
A datatable is part of a dataset (a dataset is a collection of datatable)., and is a client side representation of (some or all) of a database table.
To work with database data you use a tableadapter to transfer it from the database table to the datatable. You work with it, maybe edit it and maybe save it back to the database
From what you've described it sounds like you're not actually using a tableadapter to fill the datatable before inspecting it for data. The dataset designer is just a visual representation of the tableadapter and associated datatable classes; it doesn't mean that the database data is automatically available in your program
You'll need to have a code like:
Dim ta As New YourDatasetNameTableAdapters.SP_Get_Data_Communication_ParametersTableAdapter()
ta.Fill(Me.Ds1.SP_Get_Data_Communication_Parameters, any, parameters, the, sproc, needs, here)
Then you can look through the datatable and see the data the TA downloaded
Edit footnote:
If you make changes to the rows, like
For Each ro in Ds1.SP_Get_Data_Communication_Parameters
ro.FirstName = "John"
Next ro
Then you can send the changes back to the db using the Update method of the table adapter
at.Update(Ds1.SP_Get_Data_Communication_Parameters)
Update will run all different kinds of query, not just UPDATE. Newly added rows will be INSERT. Deleted rows will be DELETEd. Microsoft should really have called it SaveChanges

DataGridView bound to MS Access table with Autonumber Primary key causes Concurrency errors

A have a volunteer timesheet data entry system which allows the volunteers to enter the times they have spent on various activities. I used the VB.net Designer to create the system (OK, I know now that that was not a good move!) so please don't ask me to show my code, most of it is generated by the Designer. My problem is this:
Each new record is assigned a negative number as a primary key when it is entered which is the way a dgv works with Access Automumber keys. I am executing the following statements in the RowValidating event when the row is valid.
a_dgv.EndEdit()
a_dgv.CommitEdit(DataGridViewDataErrorContexts.Commit)
Me.TimeSheets2BindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.MembershipDataSet)
This code does not update the primary key value on the dgv although it does so in the Access table. If a user then attempts to delete or alter a record he has earlier added in the same session the update fails with a concurrency error. The only answer if have found to this problem is to refill the whole table. This is obviously not a desirable solution. Does anyone have a proven tested one?
I should probably mention that my table has two databound comboboxes
I was under the impression that a datagrid that is the result of a dataset from say Access does not show the PK values as -1, -2, -3.
If you created the disconnected dataset (or datatable) in code from a fill (pull data from Access), then each row normally does not show the PK.
However, regardless of the above, assuming you entered 5 rows, and now need to see the PK values?
You will during data entry in the grid should see this:
In above, I have added two rows. Your save code is somewhat like this:
tblHotels = DataGridView1.DataSource
rstDataReader.Update(tblHotels)
tblHotels.AcceptChanges()
That will send the data back to SQL server (or Access), and the autonumber PK 'ids are then generated. However, such changes are NOT pulled back into the dataset/datatable. In other words, the PK id's are generated in the database, but UNLESS you re-pull the data, you are not going to see the PK values.
You WILL have to re-pull the data. However, you can keep the current position of the grid, and re-fill the data like this:
rstDataReader.Update(tblHotels)
tblHotels.AcceptChanges()
Dim MyTop As Integer = DataGridView1.FirstDisplayedScrollingRowIndex
tblHotels.Clear()
rstDataReader.Fill(tblHotels)
DataGridView1.FirstDisplayedScrollingRowIndex = MyTop
And then you should see this:
The other way would be to send + update each row as you edit data, and then pull the PK, but obvious then you not be able to update the all your grid changes with a SAVE button, and thus of course no un-do ability.
I find the above that re-positions the top of the grid does not flicker. On the other hand, I suppose this could/would depend on how large the data set is (but then again, loading up a grid with too many rows is less then ideal).
So, as far as I can tell, you have to re-pull the dataset/datatable to get the new generated PK id's, or you have to save + pull for each row you edit. For a gridview with even several 100 rows, I don't see any flicker with the above code.

How to continuously update DataTable with new data streaming in?

I am dynamically updating my DataTable with data coming in over a websocket from my server. I would like it to only display 20 or so rows of data and after 20 rows is reached to start deleting the first row at the same rate it adds a new row so that the table is continuously updating with the latest 20 data points streaming in. So its like a continuous feed. I need to use DataTable and not dataTable so fnDeleteRow will not work. Is there any other builtin in function I can use to achieve this affect, either to continually delete the first row or another function altogether that will allow for the same affect?

Access Query not updating fast enough

Im building a key inventory mgmt system. I've created a query that show's me keys currently not in use by identifying which keys have been returned, aren't lost or have never been rented. I copied this query into the look up field for key_id in my keyActivity table (used to record key sign outs). The issue is that the query does not update to provide available keys until the table keyActivity is closed and opened again
Example: I open keyActivity, indicate that key_id = 5 is lost. When I go to a new record and select the key to sign out, key_id = 5 is presented as being available. It is not until I close the table, open it again, that key = 5 is removed from the list.
Here you can see key 5 is indicated as lost in id 5 but in id 7 when selecting a key, 5 is available when it shouldn't be.
Is there anyway to fix this or set this up to work as intended. I plan on using forms to present all the information. Is there a form solution perhaps?
The suggestion you would be better off with a Form to change table data. It can be easily requery-ed to update the table according to the changes you make and to display the udpated data accordingly. Please also read on the given references for further info.
In terms of data updating and locks in a multi user environment this article could be helpful.
"Access is NOT a database server. It's a desktop database. It has been pushed to the limit to support mutli-user environments, but only in the sense that you can share the "back end" database across a network."
...
...
"Even the record locking is performed by the Front End. All of the front end
database applications share the "lock file" (a file with the same name as
the database file, but with the extension LDB); but that file is simply a
mechanism that the front ends use to determine which front end can make
changes to the database."
....
Here is a difference between requery and refresh:
Me.Requery forces the entire recordset (underlying data) for the form to reload. This means ALL of the records in your current form will reload. Your current position will be lost, so if you're sitting on record 10 of 100, you'll find yourself back on the first record. Me.Requery is essentially the same as closing and reopening the form. Any new records added by other concurrent users will be available. Likewise any records that have been deleted will disappear. Requery essentially "re-runs the query" that pulled the data into the form in the first place. You can also use requery to update the data in a list box or combo box.
Me.Refresh saves the current record that you're working on. It will also retrieve any changes (but not additions or deletions) to any records shown in the current form. Any calculations on the form (unbound fields) are recalculated. Refresh does NOT reload the recordset. You do not lose your position in the form (you stay on the current record). Any new records added by other users will not be shown.
Reference
MS Access - Write to Table Immediately After Changing Value in Form