I have a datatable called DTAllCustomers which I update on a record by record basis, however I now need to change one field in nearly all records of the datatable. I want to quickly update the database with the updated info in the datatable.
I have tried:
Dim objCmdBuilder As New SQLiteCommandBuilder(AllCustomersAdapter)
DTAllCustomers.AcceptChanges()
AllCustomersAdapter.UpdateCommand = objCmdBuilder.GetUpdateCommand()
AllCustomersAdapter.Update(DTAllCustomers)
Each command is processed, however the underlying database is not altered?
I have searched for some time but can't find what I am doing wrong.
Any help is greatly appreciated
Brad
The issue is the fact that you are calling AcceptChanges. Once you have accepted the changes, there are no more changes, so what is Update going to save? That Update method already calls AcceptChanges implicitly once it has saved those changes to the database. It's very rare that you will have to call AcceptChanges explicitly. Here are some scenarios where you might manipulate the accepting of changes.
When you call Fill on a data adapter, the method will internally add a bunch of DataRows to the DataTable. In that case, the RowState of each will be Added, just like when you add a row yourself. That's generally not what you want when retrieving data though, so Fill implicitly calls AcceptChanges by default. Let's say that you want to retrieve data from one database and insert it into another. In that case, you do want all the rows to be Added. To do that, you set the AcceptChangesDuringFill property of the data adapter to False.
You have a single DataTable containing changes that you want to save to two different database tables. In that case, you would need two different data adapters. You would need the first Updte call to not accept the changes so they are still available to the second data adapter. In that case, you set the AcceptChangesDuringUpdate property of the first data adapter to False and the changes remain after calling Update. You might allow AcceptChanges to be called implicitly by the second data adapter or you might set AcceptChangesDuringUpdate on it too and then call AcceptChanges explicitly.
You have two DataTables in a parent/child relationship and you want to save changes from both to the database. In that case, you need to save child deletes before parent deletes and save parent inserts before child inserts. To do that, you would call GetChanges on the DataTables to get subsets to save in the appropriate order. Because you're not passing the original DataTables to Update, their changes won't be accepted implicitly, so you need to call AcceptChanges explicitly on each DataTable after a successful save.
Related
I want to know if the rows have changed values in my Datatable, how to check that?
I got a Datatable has data and I make some changes on this data by grid view so Can I check which rows have changed.
Each DataRow has a RowState. When you populate a DataTable by calling Fill on a data adapter, all rows start out Unchanged. When you add a row the value will be Added, when you edit an existing row the value will be Modified and when you delete an existing row the value will be Deleted.
Note that, when you call Fill on a data adapter, the rows will be initially added with a RowState of Added but then AcceptChanges is called and they will all be set to Unchanged. If you set AcceptChangesDuringFill to False first, they will all remain Added. This is useful when, for instance, you want to retrieve from one database and then insert into another.
When you call Update on a data adapter to save changes, AcceptChanges is called afterwards and all Added and Modified rows become Unchanged and all Deleted rows are removed. If you set AcceptChangesDuringUpdate to False first, they will all remain as they were. This is useful when, for instance, you want to save changes to multiple tables from one DataTable. You would then call AcceptChanges manually if you wanted to keep using the DataTable.
Note that each DataRow contains two versions of its data: original and current. When you get data from a field in a DataRow you will get the current version by default, but you can specify which version you want. If the RowState is Added then the original version is empty and, if the the RowState is Deleted then the current version is empty. When AcceptChanges is called, the current version is copied over the original version in rows that were Added or Modified.
You can also call GetChanges on a DataTable and that will either return Nothing or a new DataTable containing just the changed rows. You can also specify one or more DataRowState values and get just changes of that type.
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.
I have an Infragistics Ultragrid on a winform. How can I tell if a row is added or just modified? The DataChanged property will just tell me if Data has changed, not if it was added (so I can put the row data in an SQL Insert statment) or if it was modified (so I can put it in an UPDATE sql statement.)
For Each row As UltraGridRow In GroupMetadataGrid.Rows
If row.DataChanged Then
Debug.WriteLine("Saving Changed Row")
End If
Next
Thanks in advance.
UltraGrid does not track if the row has changed or was added. Look at the answer from Mike Saltzman in this thread in their forum.
This is his answer:
The grid does not track this. If you edit a row in the grid and move to another row, all changes in the previous row are committed to the data source.
The grid doesn't deal with the data base directly, it only deals with it's local data source. So if you are looking to track changes in the data source that need to be written to the database, then the data source needs to take care of that. The DataSet and DataTable class have built-in support for tracking pending changes.
The only thing you might have to be concerned about with the grid is that the current ActiveRow in the grid may have pending changes that need to be written to it's DataSource. For this you can use DataChanged. The grid will automatically commit changes when it loses focus, but if you need to manually force the changes to be committed, you can use the grid.UpdateData method, or the Update method on any individual row.
I promise I've looked at the bazillion posts claiming this same issue, so please forgive me that I am still stumped.
I have a VS2008 smart device project containing a strongly-typed dataset. The user is allowed to input values and save them, each time creating a new record in the dataset. Should the user wish to edit values, a modal child form is displayed with several combo boxes containing the possible values for acceptable input to edit the row. Each combo box is bound in the constructor for the child form in the fashion below:
With cmbSize
.DataSource = frmMain.dstConfig.Sizes
.DisplayMember = "Display"
.ValueMember = "Value"
.DataBindings.Add("SelectedValue", trowNewRow, "SIZE", True, DataSourceUpdateMode.OnPropertyChanged)
End With
dstConfig is a dataset of tables containing the constraints. Binding to the source's display and value members works fine.
trowNewRow is a reference to the row in the dataset conveyed from the main form when the user initiates an edit procedure, to the child form by passing the row as a parameter "ByRef". The strong-typing is preserved. It occurred to me there may be some sort of a disconnection that I am not aware of when this occurs. I pass the reference by performing a "Select" procedure on the dataset and filtering by a unique ID field corresponding to the row that is to be edited. The result is a single-item array of strongly-typed rows of the same schema as the dataset's from which I pass the first (and only) item as my object.
After the user submits the changes by clicking an OK button, a procedure is triggered to evaluate whether or not a change was actually performed. The best way I thought to do this was by checking the RowState of trowNewRow. The RowState, however, remains "Added" regardless of the changes. Manually checking the values of the row indicates the changes have indeed been recorded. I've verified the AcceptChanges procedure of trowNewRow is not explicitly being called by any of my code.
I have tried the following:
1.) Calling the EndEdit procedure of trowNewRow
2.) Manually performing a WriteValue on the combo boxes
3.) Calling the EndCurrentEdit procedure on the combo boxes' BindingManagerBase objects
4.)Every combination of the above
Thank you in advance for any ideas or solutions.
A DataRow contains two sets of data - original and current - and the RowState reflects the relationship between them. If there is no original data but there is current data then the RowState is Added. As #Plutonix says, no amount of editing the current data is going to add original data so the RowState remains Added even if you make further changes. If there is no current data but there is original data then the RowState is Deleted. If the current data matches the original data then the RowState is Unchanged, otherwise it's Modified.
When you call Update on a data adapter or table adapter, the InsertCommand is executed for each Added row, the UpdateCommand is executed for each Modified row and the DeleteCommand is executed for each Deleted row. After a successful save, the adapter implicitly calls AcceptChanges. That will remove all Deleted rows from the DataTable and copy the current values in Added and Modified rows over the original values, changing the RowState to Unchanged.
So, the RowState is for tracking changes since the last save to the database. You can't use it to determine whether the user has made any changes in the UI unless you are saving those changes to the database after every edit. If you want finer-grained change-tracking then you have to implement it yourself. Personally, I don't bind in those cases but wait until the user clicks OK to push the data into the DataRow. That also allows you to cancel the latest edit without losing prior edits.
I'm not an Access expert, but am an SQL expert. I inherited an Access front-end referencing a SQL 2005 database that worked OK for about 5000 records, but is failing miserably for 800k records...
Behind the scenes in the SQL profiler & activity manager I see some kind of Access query like:
SELECT "MS1"."id" FROM "dbo"."customer" "MS1" ORDER BY "MS1"."id"
The MS prefix doesn't appear in any Access code I can see. I'm suspicious of the built-in Access navigation code:
DoCmd.GoToRecord , , acNext
The GoToRecord has AcRecord constant, which includes things like acFirst, acLast, acNext, acPrevious and acGoTo.
What does it mean in a database context to move to the "next" record? This particular table uses an identity column as the PK, so is it internally grabbing all the IDs and then moving to the one that is the next highest???
If so, how would it work if a table was comprised of three different fields for the PK?
Or am I on the wrong track, and something else in Access is calling that statement? Unfortunately I see a ton of prepared statements in the profiler.
THanks!
First is literally the first row in the Recordset. In general, Access accesses data via the equivalent of cursors. So, Next and Previous are moving forward and backwards in the Recordset one row at a time just as you can with SQL Server's cursors. Be careful about depending on the sequence of the rows without an ORDER BY statement in the construction of the Recordset. Although Access is an ISAM, you should not rely on the rows coming in any particular order. Depending on the cursor type, Access will not pull the entire table down but will generally ask for one record at a time. That said, I have seen Access pull entire tables for whatever reason.
You have to distinguish between automating Access objects and working with recordsets in code.
In a form, this command has meaning:
DoCmd.GoToRecord , , acNext
It is nonspecific, and it is not predictable what record it will go to unless you know the ordering of the underlying recordset in the form and the starting record. It navigates you through the recordset stored in the form's edit buffer (which is loaded in the OnOpen event of the form). The command would be used, for instance, in the code behind a command button whose purpose is to navigate records loaded into the form that currentlyl has the focus. I would never leave out the optional arguments if I were to use that command (I almost never do). Instead, I'd identify the specific form I wanted it to apply to:
DoCmd.GoToRecord acForm, "MyForm", acNext
In traversing a DAO recordset, .MoveNext likewise has no predefined meaning except if you know the ordering and starting record. When you are walking a recordset (something you shouldn't do very often, since it's pretty inefficient; but it depends on the task you need to perform) and need to hit each record, you'd certainly call .MoveNext as part of your loop:
With rs
.MoveFirst ' technically not required, as it's the default starting point
Do Until .EOF
[do something]
.MoveNext
Loop
End With
Nothing mysterious there. It's most likely going to be used in code with small numbers of records (large recordsets really oughtn't be navigated sequentially).
In answer to your specific question:
What does it mean in a database
context to move to the "next" record?
This particular table uses an identity
column as the PK, so is it internally
grabbing all the IDs and then moving
to the one that is the next highest???
...as I said, the next record is determined by the ordering of the recordset being traversed and the starting position. In the case of the form, it's the edit buffer that's being traversed, and as the current record bookmark changes in the edit buffer, the form is updated to load the data for that record. The dynaset is bound to the underlying data table, and when the form's edit buffer is saved, the edited data is written back to the server. While it's being edited, locks may or may not be maintained on the record on the server, but Access/Jet/ACE does keep track of the state of the existing record on the server and the record in the edit buffer and will inform you at save time in Access if the record on the server has been changed since it was loaded into the form's edit buffer.
Now, in a comment, you say the form is bound to the whole table. This is a terrible design no matter whether your data is stored in a Jet/ACE back end data file or in a server database like SQL Server. The only reason Access can get away with it is because it and Jet are rather efficient about pulling data from the data source.
I properly-designed client/server Access front end will not load full tables in forms, but instead ask what filtered recordset you want to load, either 1 or more records at a time. This is only marginally more complicated than binding to a whole table.
As to knowing what cursor types are being used, you shouldn't be worrying about it. By default, Access forms use what Access/Jet/ACE calls dynasets. Each form has a RecordsetType property, and it's set to dynaset by default (read the help file on the meaning of the different recordset types). If you want more control of that, you can (but likely shouldn't) create your recordsets in code and assign them to the form's .Recordset property. This is useful in a few circumstances, such as when you'd like to bind a form to a disconnected recordset, but the point of Access is leveraging its capabilities working with bound data. Assigning your own recordsets still gets you bound controls, and the form events, but is more work than is usually necessary.
Basically, change your forms to load only the subset of records the user needs to work with (that may be one record at a time), and then let everything else get done with Access's default behaviors. If something causes a bottleneck, then troubleshoot that and replace the default behavior with something more efficient.
In other words, avoid premature optimization -- let Access be Access.
And don't worry about what Access is doing behind the scenes unless/until Access does something inappropriate.