How update a SQL table from a modified datatable? - vb.net

I used the DataSet Designer to create FTWDataSet which holds the AlarmText table from a SQLExpress database. This far my form contains ONLY Datagridview1. The code below successfully shows the contents of the AlarmText table plus the one added checkbox column (which I will populate with display-only data, and is not an issue here).
Dim ta As New FTWDataSetTableAdapters.AlarmTextTableAdapter
Dim dt As New FTWDataSet.AlarmTextDataTable
ta.Fill(dt)
DataGridView1.DataSource = dt
'create a new Bool column in the datatable
dt.Columns.Add("NewCol", (New Boolean).GetType)
What else do I need to do to use the DataGridView to edit and save values in the AlarmText table?

Here's a brief MSDN walkthrough on this topic.
Some notes:
You shouldn't need a binding source to persist changes back to the database.
To make the table adapter accessible to other procedures in your form, make it a form-scoped (a.k.a. member) variable instead of method-scoped, as in your example.
If you created your Dataset using the Dataset Designer, and you're fetching your original data from anything more than a simple table or view, then your adapter won't know how to update anything in the original database. You have to manually configure the UPDATE command in this situation. For reference, see the TableAdapter Update Commands section in the above link.
I should also mention that I avoid TableAdapters in ADO.Net like the plague. In theory they're very convenient and powerful. In practice, many of them (especially the one for the Oracle provider) are buggy, and if they don't work exactly right you're totally screwed.
If you're pulling from just one underlying table, the adapter should work fine (so ignore my nihilistic advice for the time being). It may be that adding an additional column in code will breaks the adapter (since there's no corresponding column in the database table).

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

Update access backend schema with vba code

I have created an application in ms access with vba code. The app is split in front end and back end. Back end holds the user data. Now i want to add some new features but this needs some excessive back end changes in the schema structure.
My question is, what is the best way or practice to deliver to the end user the changes i want every time I upgrade my application? Of course it has to be something that keeps the current data intact.
I have seen some suggestions about a free program called CompareEm that compares the old and new database, and produces the appropriate vba code to do the job.
I was also considering if it would be more convenient to copy an empty database that has the wanted schema alongside the uograded frontend and having a module that compares the old database of the user with the empty one and try to change the old schema according the new one. (First my removing all relations, converting the tables, and then reapplying the new relations).
In any case i would like something that could be done automatically or by some customer code, in order to avoid messing up with users data.
Any help is appreciated.
This is one weak spot of Access. We don't have a really great scripting language and system built that allows scripting out of database "schema".
So while the split design is a must have, and makes update of the code and application part very easy, a update of the back end (BE) is a challenge.
I have done this in the past. And the way you go about this?
Well, you have to make up a rule, and the rule is this:
Anytime you add a new field/column, a new table?
You MUST write the change(s) into a code module. In other words, you NEVER use the GUI anymore to add a new column (or say change the length). You code to that sub that will do this for you. In fact, in one application I had (many customers, all 100% Access runtime).
So, to add new features, changes, fixes? I often had to add a few new columns or tables. So what I would do is WRITE the code into that "change/update" routine. (and I also passed it a version number). On startup, that code would be called.
It started out with say about 5 lines of code. A few years later? I think it had well over 100 lines of code. And worked very well. If they opend a older data file (the BE), then if it really was a older verison - or say they had not paid for upgrades, then their current verison of software could and would be several upgrades behind. But, as noted, since on startup all of the "updates" to the database were ALWAYS written as code, then even if they were say 5 versions back, then all of the version numbered code would run, make the changes to the BE, and they would be up to date.
So, this means you have to code this out. It not all that hard, but you DO HAVE do adopt this way of working.
So, the code in that module looked like this:
There were two common cases. The first case was a WHOLE new table. I did not want to write out the code to create that table, so what I would do is INCLUDE the whole new table in the FE for this purpose (and I would append the letter "C" to the table).
So, the code on startup would check if the table exists, and if it does not, then I would execute a transfer command to copy the table. The code stub looked like this:
' check for new table tblRemindDefaults
On Error GoTo reminddefaultsadd
Set rst = CurrentDb.OpenRecordset("tblRemindDefaults")
So, in above, I set the error handler and try to open the table. If the table does not exist, then above calls the routine remindDefaultsAdd. Its job of course is to add this new table to the BE.
The above code stub looked like this:
remindadd:
Dim strFromDB As String
Dim strToDB As String
Dim strSql As String
Dim cp As Object
Dim shellpath As String
strFromDB = CurrentProject.FullName
strToDB = strBackEnd
DoCmd.TransferDatabase acExport, "Microsoft Access", strToDB, acTable, "tblGroupRemindC", "tblGroupRemind", True
Note how then I simply copy a table from the FE to the BE.
The next type of upgrade was adding a new column to a existing table.
And again, similar to above, the code would check for the column, and if not present, I would add the column. In this case we not creating a new table, so no copy of the table.
Typical code looked like this:
' add our new default user field
Dim nF As dao.field
Dim nT As dao.TableDef
Dim nR As dao.Relation
strFromDB = CurrentProject.FullName
strToDB = strBackEnd
Set db = OpenDatabase(strToDB)
Set nT = db.TableDefs("tblEmployee")
nT.Fields.Append nT.CreateField("DefaultUser", dbText, 25)
nT.Fields.Refresh
Set nT = Nothing
So in above we add a new field called DefaultUser to the table.
If you OPEN DIRECT the BE, and do NOT use linked tables, then you are free and able to modify the table in question with code. You can ALSO use SQL ddl's statements. So while I noted that scripting support in Access is not all that great, you can do this:
Set db = OpenDatabase(strToDB)
strSql = "ALTER TABLE tblEmployee ADD COLUMN DefaultUser varCHAR(25);"
CurrentDB.Execute strSQL, dbFailOnError.
So, you can write out code using table defs, create the field and add it. Or you can use SQL DDL's statements and execute those. Of course each upgrade part I wrote had a version number test.
So, the simple concept is that every time you need a new column, new table etc.?
You write out this code to make the change. So, you might say work for a month or 2 add a whole bunch of new features - and some of these features will require new columns. So you NEVER use the GUI to do this. You write little code routines in your upgrade module, and the run them. Then keep on developing. So, when you done, all your updates to the table(s) are now done, and you can deploy the new FE to those users. On startup, the update code will run based on version number. (I have a small one row table in the FE with version number, and also a small one row table in the BE that has version number). After a upgrade, the new columns, new tables are now in the BE, and then of course I update that version number.
So, you can use a combination of SQL ddl commands, or use code to create the field def as I did above.
The only really important issue is that table changes can NOT be made against linked tables. So you have to create + open a SEPERATE instance of the database object as I did per above. And on startup, you have to ensure that no main form that is bound to a linked table has or is running yet. (since that will open the BE, and you can't make table changes against a open table).
So, it not too hard to make the updates. But the HARD part is your self discipline . You have to ALWAYS go to your upgrade routines and add the new column in code. So that way after working for a few weeks, you have coded out the table changes, and thus are able to re-run that code against older existing BE's out in the field.
Welcome to Stack Overflow! When you send out a new version of your front-end db, you would need either to (1) include a script to update the backend db, or (2) a fresh copy of the back-end db and a script to transfer the data from the previous version. In either case, you need to consider customers who may have skipped an update, so you would need some kind of version stamp on the back-end db.
You can accomplish either strategy with Access DDL (see here: https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/data-definition-language) and/or Access DAO (see here: https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/microsoft-data-access-objects-reference).
Please let us know if you have any specific questions but keep in mind SO is not intended for offering general advice or product reviews.

How to pass data from DataGridView to table in ReportViewer without Database or Dataset

I have a few DataGridView tables which user fills (I don't use any Dataset or Database here).
I need to pass them to ReportViewer, I want exact the same tables in report as in a form, no changes at all.
I tried something like this, but it's not working:
Dim rds As New ReportDataSource("rds1", Form1.dgv1.DataSource)
Me.ReportViewer1.LocalReport.DataSources.Add(rds)
and in report1.rdlc added a table and set it dataset name to rds1 but it generates error
The table ‘table1’ refers to an invalid DataSetName ‘rds1’.
So I guess I need to make a DataSet from my DataGridView tables.
How would you suggest me to do that? Otherwise, is there any simpler way to pass the table to report?
You should add a DataSet to your report .rldc Data Sources list and drag and drop the columns into your report element (table, tablix, list etc.)
Remember you can add any kind of object from your project to your report as Data Source (basically any Public Class you create). The imported names and types will be taken from the class properties.

Update DataGridView bound to DataTable

Forgive me for this seemingly obvious question, but my searches have not revealed the answer.
I have a DataGridView bound to a DataTable. Although all rows are displayed in the grid, the table represented by the Grid's DataSource does not get updated after changing values in one or more DataGridView columns. Both grid and table contain a Primary Key. What am I missing here? Do I need to do something in the RowValidated event?
dgvCriticalContacts.AutoGenerateColumns = False
Dim ccRs As DataTable = Common.OpenRecordset("SELECT * FROM PhoneList")
dgvCriticalContacts.DataSource = ccRs
A DataGridView does not have the ability to automatically update the database you are getting your information from. While you can use a DataTable to give the DataGridView a data source, it also does not have the ability to update itself back to the database.
However, the .Net Framework does have a TableAdapter ( http://msdn.microsoft.com/en-us/library/bz9tthwx%28v=vs.90%29.aspx, 3.5 and earlier) or a DataAdapter ( http://msdn.microsoft.com/en-us/library/system.data.common.dataadapter%28v=vs.90%29.aspx, All versions) that can perform these functions.
My personal experience is that these adapters very quickly cause more problems than they solve if it is anything more a simple data structure. If it is anything more complex, you are better off creating a class for your rows and making a BindingList (or something similar) to allow the DataGridView to display them. You will have to implement your own change tracking and CRUD statements, but it's well worth it and will likely cause less of a headache in the long run.