<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://www.dotnetzone.gr:443/cs/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Web Services</title><link>https://www.dotnetzone.gr:443/cs/forums/21/ShowForum.aspx</link><description>Συζήτηση περί των web services και WSE, την υλοποίησή τους με το .NET Framework, καθώς και θέματα interoperability με άλλα συστήματα</description><dc:language>el</dc:language><generator>CommunityServer 2.1 SP3 (Build: 20423.1)</generator><item><title>Re: Update a dataset with related tables and identity columns by using a Web service</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3522.aspx</link><pubDate>Sat, 16 Jul 2005 19:31:59 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3522</guid><dc:creator>Χρήστος Γεωργακόπουλος</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3522.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3522</wfw:commentRss><description>&lt;p&gt;Έκανα τις απαραίτητες διορθώσεις στον κώδικα, επέκτεινα το documentation, μίκρυνα τον τίτλο για να μην φωνάζουν μερικοί μερικοί, έβαλα attached τον κώδικα.&lt;br /&gt;&lt;br /&gt;Ίσως ο κώδικας αυτός θα έπρεπει να μπεί κάπου στο Data Access Application Block μέσα στο Enterpsise Library... Θα το ψάξω με τους τυπάδες που το φτιάχνουν...&lt;/p&gt;</description></item><item><title>Re: How to update a dataset with related tables and identity columns from a Windows Forms application by using a Web service in Visual Basic .NET</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3445.aspx</link><pubDate>Wed, 13 Jul 2005 03:40:27 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3445</guid><dc:creator>KelMan</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3445.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3445</wfw:commentRss><description>&lt;img src="/forums//emoticons/emotion-2.gif" alt="Big Smile" /&gt; Επίσης, μην βάζετε τεράστιους τίτλους στα posts γιατί "σπρώχνουν" τα κουμπιά και τα links δεξιά, το ένα κάτω από το άλλο...</description></item><item><title>Re: How to update a dataset with related tables and identity columns from a Windows Forms application by using a Web service in Visual Basic .NET</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3443.aspx</link><pubDate>Wed, 13 Jul 2005 03:14:39 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3443</guid><dc:creator>George J. Capnias</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3443.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3443</wfw:commentRss><description>&lt;font face="Georgia" size="2"&gt;Έκανα ένα γενικό καθάρισμα στα δικαιώματα των&amp;nbsp;forums σήμερα. Όταν θα πάς να κάνεις post μήνυμα θα πρέπει να έχεις το δικαίωμα να βάλεις και ένα attachment. &lt;br /&gt;&lt;br /&gt;&lt;u&gt;Προσοχή&lt;/u&gt;: Για όλους πάει αυτό, αν θέλετε να βάλετε ένα "&lt;strong&gt;συννημένοt&lt;/strong&gt;"&amp;nbsp;&lt;img src="/forums//emoticons/emotion-1.gif" alt="Smile" /&gt; σε ένα μύνημα, επιλέγετε το αρχείο και μετά κατευθείαν post, όχι preview, για να μπορέσει να το κάνει επιτυχημένα. Είναι ένα bug του forum.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;George J.&lt;br /&gt;&lt;/font&gt;</description></item><item><title>Re: How to update a dataset with related tables and identity columns from a Windows Forms application by using a Web service in Visual Basic .NET</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3433.aspx</link><pubDate>Tue, 12 Jul 2005 21:27:12 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3433</guid><dc:creator>Χρήστος Γεωργακόπουλος</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3433.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3433</wfw:commentRss><description>&lt;p&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;Βρήκα κάτι που νομίζω ότι είναι λάθος στον αρχικό κώδικα της Microsoft:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;The correct order should be Insert, Update, Delete. Please consider the following case:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;On the client:&lt;br /&gt;1. A record is added in the parent table&lt;br /&gt;2. A record from the child table is updated so that it relates to the record of the previous step.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;When you call UpdateData, the record from the child table (step 2) is updated to refer to a record from the parent table that do not yet exists is the database because the insert operation follows the update. &lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;So the code should be:&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font style="BACKGROUND-COLOR: #efefef"&gt;daOrd2.Update(ds.Tables("orders").Select("", "", DataViewRowState.Added))&lt;br /&gt;ds.EnforceConstraints = False&lt;br /&gt;daDetails.Update(ds.Tables("details").Select("", "", DataViewRowState.Added))&lt;br /&gt;ds.EnforceConstraints = True&lt;br /&gt;&lt;br /&gt;daOrd2.Update(ds.Tables("orders").Select("", "", DataViewRowState.ModifiedCurrent))&lt;br /&gt;daDetails.Update(ds.Tables("details").Select("", "", DataViewRowState.ModifiedCurrent ))&lt;br /&gt;&lt;br /&gt;daDetails.Update(GetDeletedRows(ds.Tables("details")))&lt;br /&gt;daOrd2.Update(GetDeletedRows(ds.Tables("orders")))&lt;br /&gt;&lt;br /&gt;&lt;strike&gt;Οπότε και στον δικό μου κώδικα πρέπει να αλλάξει αντίστοιχα η σειρά. Μόλις το τεστάρω λίγο ακόμα και ο Γιώργος μου ανοίξει το upload θα ανεβάσω τις σχετικές τροποποιήσεις.&lt;br /&gt;&lt;br /&gt;&lt;/strike&gt;Η διόρθωση αυτή έχει πλέον ενσωματωθεί στο αρχικό post&lt;br /&gt;&lt;/p&gt;&lt;/font&gt;</description></item><item><title>Re: Update a dataset with related tables and identity columns by using a Web service</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3428.aspx</link><pubDate>Tue, 12 Jul 2005 14:53:09 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3428</guid><dc:creator>Χρήστος Γεωργακόπουλος</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3428.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3428</wfw:commentRss><description>&lt;strike&gt;Το κλασσικό... upload δεν βλέπω πουθενά. Anyway, το έβαλα online μέχρι να δεις τι γίνεται με το upload:&lt;br /&gt;&lt;/strike&gt;&lt;br /&gt;Ο κώδικας είναι πλέον attached στο αρχικό post</description></item><item><title>Re: How to update a dataset with related tables and identity columns from a Windows Forms application by using a Web service in Visual Basic .NET</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3427.aspx</link><pubDate>Tue, 12 Jul 2005 09:17:04 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3427</guid><dc:creator>George J. Capnias</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3427.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3427</wfw:commentRss><description>&lt;font face="Georgia" size="2"&gt;Χρήστο,&lt;br /&gt;&lt;br /&gt;Μπράβο πολύ καλή προσπάθεια! &lt;img src="/forums//emoticons/emotion-21.gif" alt="Yes" /&gt;&lt;br /&gt;&lt;br /&gt;Προσπάθησα να κάνω copy &amp;amp; paste τον κώδικα στο Visual Studio IDE αλλά δεν τα κατάφερα. Έχω αλλάξει τα δικαιώματα στο forum, και αν δεν σου κάνει κόπο, μπορείς να κάνεις upload είτε το project, ή μόνο το εν λόγω .vb αρχείο, ώστε να μπορούν όλοι να πάρουν τον κώδικα.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;George J.&lt;br /&gt;&lt;/font&gt;</description></item><item><title>Update a dataset with related tables and identity columns by using a Web service</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/3422.aspx</link><pubDate>Tue, 12 Jul 2005 03:32:56 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:3422</guid><dc:creator>Χρήστος Γεωργακόπουλος</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/3422.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=21&amp;PostID=3422</wfw:commentRss><description>&lt;p&gt;Full Title: "How to update a dataset with related tables and identity columns from a Windows Forms application by using a Web service in Visual Basic .NET"&lt;br /&gt;&lt;br /&gt;Based on the MSDN article 310350 (How to update parent-child data with an Identity column from a Windows Forms application by using a Web service in Visual Basic .NET, &lt;a target="_blank" title="http://support.microsoft.com/kb/310350" href="http://support.microsoft.com/kb/310350"&gt;&lt;u&gt;&lt;font color="#800080"&gt;http://support.microsoft.com/kb/310350&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;)&lt;/p&gt; &lt;p&gt;I wrote this piece of code to extend Microsoft’s example of a simple parent-child relationship. The function UpdateDataSet can accept as input an array of tables and their corresponding data adapters, perform all requested CRUD operations and return the dataset with rows having 'as expected' row state and version for merging back to client's datasets.&lt;/p&gt; &lt;p&gt;It is important that the data adapters and the data tables will be passed to UpdateDataSet function with a specific order, because when relations exists in the dataset and in database, the CRUD operations must be applied in the correct order. The order expected here is from the children to parent tables. For example, when we have two tables, ParentTable and ChildTable, the dataTables array must contain then in this order ChildTable, ParentTable with the same order for their data adapters in dataAdapters array.&lt;/p&gt; &lt;p&gt;The philosophy behind the code is this:&lt;/p&gt; &lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;Situation:&lt;/font&gt;&lt;/b&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;We have a data access layer (DAL) hidden behind a web services layer and a client that communicates with web services using datasets&lt;/li&gt; &lt;li&gt;The datasets contain multiple tables with relations&lt;/li&gt; &lt;li&gt;The client requests from the web services to get such a dataset filled up with data&lt;/li&gt; &lt;li&gt;The client does multiple insert, update and delete actions on the dataset that currently holds&lt;/li&gt; &lt;li&gt;The client uses GetChanges to get all changed rows and sends those changed rows (as a dataset) back to the web services layer to update the database&lt;/li&gt; &lt;li&gt;The DAL updates the database and sends back to the client (through web services) the updated rows&lt;/li&gt; &lt;li&gt;The client gets the database updated rows and merges it back to the original dataset&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;Problem:&lt;/font&gt;&lt;/b&gt;&lt;/p&gt; &lt;p&gt;When the data adapters in the DAL do an update on dataset, they force an AcceptChanges on all rows of all tables. When a row was inserted in the dataset from the client side, the dataset detects identity columns an assign them virtual identity values. These values usually are identical with the actual identity that the database will assign, except two cases:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;When a table in the client's dataset is completely empty, the dataset counter for the identity columns starts from 0&lt;/li&gt; &lt;li&gt;When another user inserts a record to the same table he gets the next available ID but the dataset on the first client thinks that this ID is still available.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;So, when the data adapters finish updating the tables, they return to the client a dataset with "UnChanged" status and different IDs from what the client's dataset currently holds. The result is duplicate rows in the client's dataset with row state "UnChanged", with some of them not really exist in database.&lt;/p&gt; &lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;Status:&lt;/font&gt;&lt;/b&gt;&lt;/p&gt; &lt;p&gt;Microsoft says that this behaviour is by design. And indeed, if you access the database directly from the client and your performance needs does not require using the GetChanges method, the data adapters will correctly update your dataset to reflect the actual database data. But when GetChanges is necessary to reduce your bandwidth consumption, you have a problem.&lt;/p&gt; &lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;Solution:&lt;/font&gt;&lt;/b&gt;&lt;/p&gt; &lt;p&gt;The main idea is to manually handle the row states and versions of all rows, after they finish processing of data adapters as follows:&lt;/p&gt; &lt;p&gt;&lt;b&gt;Row Versions:&lt;/b&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;All original row versions must exactly much the rows that the client currently holds, so that the Merge method on the client correctly understands witch rows to update in the original dataset.&lt;/li&gt; &lt;li&gt;All current row versions must exactly much the actual database rows, so that after the Merge and AcceptChanges on the client the dataset data will reflect the actual data in the database&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;b&gt;Row States:&lt;/b&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;When a row is inserted we'll need to manually set the row version as previously described and maintain the Added row state of the row&lt;/li&gt; &lt;li&gt;When a row is deleted we'll need just to maintain the Deleted row state so that final Merge and AcceptChanges methods will remove the row from the dataset&lt;/li&gt; &lt;li&gt;When a row is updated we don't want to change anything, the Merge operation will correctly update the row in the client's dataset&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;&lt;b&gt;&lt;font size="4"&gt;Points of interest:&lt;/font&gt;&lt;/b&gt;&lt;/p&gt; &lt;p&gt;&lt;b&gt;Error Handling&lt;/b&gt;&lt;/p&gt; &lt;p&gt;In order to manually process the row states and versions of rows while the data adapters are updating them, we use the RowUpdated event of each data adapter. Microsoft's example starts with direct examination of the StatementType of each event (to see what the actual action on the row was). That's not correct because when an error occurs (e.g. there is a foreign key violation with a table not included in the dataset) the status set on the event arguments UpdateStatus.SkipCurrentRow will completely hide the actual exception. So I choose to examine the Errors parameter and do nothing if an error has occurred, so that I get a proper exception in those cases.&lt;/p&gt; &lt;p&gt;&lt;b&gt;Order of insert, update, delete&lt;/b&gt;&lt;/p&gt; &lt;p&gt;Microsoft chooses to process the row actions in this order: delete, modify, insert. That is very convenient because: a. When rows are deleted, their row state does not change, b. When the rows are modified, their row state does not change, c. When the rows are inserted, we manually set their row state to modified so that the client understands that these rows were modified compared to the rows that currently holds. At this point, we don't mind to set the row state to modified, because we already know that the modification of rows is already complete. But this processing order is not correct for all cases:&lt;/p&gt; &lt;p&gt;There are two cases that uniquely specify the order of actions that must be performed:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Deletion of records must occur before inserts&lt;br /&gt;Why: If a table has a column with unique values, and in the dataset you delete a row and then reinsert it with the same value for the unique column, then the new row cannot be inserted without deleting first the old row. &lt;/li&gt; &lt;li&gt;Modification of existing records must occur after inserts&lt;br /&gt;Why: If in the dataset you insert a new row in a parent table and then update a record from a child table to refer to the newly inserted parent row, you cannot update the child row in the database if the parent row not yet exists.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Conclusion:&lt;/p&gt; &lt;p&gt;The only possible order of actions (generally speaking, if we want to allow all types of actions when updating the dataset) is:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Delete (starting with children tables first, continuing up to the parent tables)&lt;/li&gt; &lt;li&gt;Insert (starting with the parent tables first, continuing down to the children tables)&lt;/li&gt; &lt;li&gt;Update (starting with the parent tables first, continuing down to the children tables)&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;But, when the order is Delete, Insert, Update, a new problem arises. Let’s examine what happens again:&lt;/p&gt; &lt;p&gt;a. When rows are deleted, their row state does not change, b. When the rows are inserted, we manually set their row state to modified so that the client understands that these rows were modified compared to the rows that currently holds, c. When we try to modify the rows, we won't know which rows have to actually be updated in the database and which rows have row state Modified because we manually set the so in the previous step.&lt;/p&gt; &lt;p&gt;So, before processing the rows in the correct order, we have to store the original action intension so we won't mess up by looking row states. For this purpose I use the custom TableRows object with three arrays to store initially inserted, updated and deleted rows.&lt;/p&gt; &lt;p&gt;&lt;b&gt;Transactions&lt;/b&gt;&lt;/p&gt; &lt;p&gt;The whole process must be executed in a transaction scope so that we can roll back the changes if an error occurs. I've put two versions of UpdateDataSet method, what with a transaction parameter so that the process can participate in an existing transaction and one without a transaction parameter that creates and uses its own transaction.&lt;/p&gt; &lt;p&gt;For any comments, suggestions, bugs, ideas etc. please post here or contact me directly at &lt;a target="_blank" title="mailto:cgeorgakopoulos@gmail.com" href="mailto:cgeorgakopoulos@gmail.com"&gt;&lt;u&gt;&lt;font color="#0000ff"&gt;cgeorgakopoulos@gmail.com&lt;/font&gt;&lt;/u&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Chris Georgakopoulos&lt;br /&gt;&lt;br /&gt;&lt;font color="#0000ff" size="2"&gt;&lt;/p&gt;&lt;pre class="source"&gt;&lt;table border="0" cellspacing="0" width="100%"&gt;&lt;tr&gt;&lt;td width="15"&gt;&lt;/td&gt;&lt;td bgcolor="lightgrey" width="15"&gt;&lt;/td&gt;&lt;td bgcolor="lightgrey"&gt;&lt;br&gt;&lt;font face="Lucida Console, Courier" size="2"&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Imports&lt;/font&gt;&lt;font size="2"&gt; System.Data.SqlClient&lt;/p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Module&lt;/font&gt;&lt;font size="2"&gt; DataAdapterHelper&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Author: cgeorgakopoulos@gmail.com&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Private&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; GetDeletedRows(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; dataTable &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataTable) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Returns an array of data rows with all the deleted rows contained in&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'dataTable&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; DeletedRows() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; dataTable &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Return&lt;/font&gt;&lt;font size="2"&gt; DeletedRows&lt;/p&gt; &lt;p&gt;DeletedRows = dataTable.Select(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/font&gt;&lt;font size="2"&gt;.Empty, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;String&lt;/font&gt;&lt;font size="2"&gt;.Empty, DataViewRowState.Deleted)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; DeletedRows.Length = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;OrElse&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Not&lt;/font&gt;&lt;font size="2"&gt; (DeletedRows(0) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt;) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Return&lt;/font&gt;&lt;font size="2"&gt; DeletedRows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; RowCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Each&lt;/font&gt;&lt;font size="2"&gt; Row &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;In&lt;/font&gt;&lt;font size="2"&gt; dataTable.Rows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; Row.RowState = DataRowState.Deleted &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;DeletedRows(RowCounter) = Row&lt;/p&gt; &lt;p&gt;RowCounter += 1&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Return&lt;/font&gt;&lt;font size="2"&gt; DeletedRows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Private&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Sub&lt;/font&gt;&lt;font size="2"&gt; RowUpdated(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; sender &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Object&lt;/font&gt;&lt;font size="2"&gt;, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; e &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; System.Data.SqlClient.SqlRowUpdatedEventArgs)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Preserves the expected versions of rows, so that merge operations can occur&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'without duplicate rows&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'If the update operation has an error, then we do not interfere so that&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'a proper exception will be thrown.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; e.Errors &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'If we have an insert operation on a row, we need to return a row&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'that has the following:&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' a. The values that the client dataset auto generated on primary key&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' columns as original state values&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' b. the values that was generated by the database for the primary key&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' columns as current state values&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' c. RowState = Modified&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; e.StatementType = StatementType.Insert &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Don't allow the AcceptChanges to occur on this row.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;e.Status = UpdateStatus.SkipCurrentRow&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Holds the number of primary key column in the row table.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = e.Row.Table.PrimaryKey.Length&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Holds the actual primary key values that were returned from the database.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; CurrentKeys(NumberOfPrimaryKeys) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Holds the initial primary key values as passed in the source dataset.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; OriginalKeys(NumberOfPrimaryKeys) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Holds the original read-only state of the primary key columns, so that&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'we can set it back after we manually update the primary key values.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; ReadOnlyKeys(NumberOfPrimaryKeys) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We get the actual primary key values that were returned from the database&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'These values are in the Current DataRowVersion of each row.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;CurrentKeys(KeyCounter) = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;CType&lt;/font&gt;&lt;font size="2"&gt;(e.Row(e.Row.Table.PrimaryKey(KeyCounter), DataRowVersion.Current), &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt;)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We get the primary key values as they passed from the source dataset.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'These values are stored in the Original DataRowVersion of each row.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;OriginalKeys(KeyCounter) = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;CType&lt;/font&gt;&lt;font size="2"&gt;(e.Row(e.Row.Table.PrimaryKey(KeyCounter), DataRowVersion.Original), &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt;)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We get the original read only state for each primary key column. We'll&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'change this to readonly = false just to be sure that we cat set the values&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'in each column and then we'll set it back to were it was.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;ReadOnlyKeys(KeyCounter) = e.Row.Table.PrimaryKey(KeyCounter).ReadOnly&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We make all primary key columns non readonly&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;e.Row.Table.PrimaryKey(KeyCounter).ReadOnly = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;False&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;' This is where you get a correct original value key stored in the row&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;e.Row(e.Row.Table.PrimaryKey(KeyCounter)) = OriginalKeys(KeyCounter)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We force accepting changes so that when we set the actual value of each&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'primary key column in the next step, the original values will be preserved&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'and the row state goes to Modified.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;e.Row.AcceptChanges()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Now store the actual primary key value back into the primary key column.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;e.Row(e.Row.Table.PrimaryKey(KeyCounter)) = CurrentKeys(KeyCounter)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'We set back the proper readonly state of each primary key column&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; KeyCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfPrimaryKeys - 1&lt;/p&gt; &lt;p&gt;e.Row.Table.PrimaryKey(KeyCounter).ReadOnly = ReadOnlyKeys(KeyCounter)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'If we have a delete operation, we only need to preserve the deleted row state&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'of the row, so we just prevent the adapter from doing an accept changes on&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'deleted rows&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; e.StatementType = StatementType.Delete &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; e.Status = UpdateStatus.SkipCurrentRow&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Sub&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; UpdateDataSet(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; dataAdapters() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SqlDataAdapter, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; dataTables() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataTable, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; transaction &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SqlTransaction) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataSet&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Execute all CRUD operations on the rows contained in dataTables, using &lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'the data adapters contained in dataAdapters.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'It is extremely important that the data adapters and the data tables&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'will be passed here with a specific order because when relations exists&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'in the dataset and in database, the CRUD operations must be applied&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'in the correct order.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'The order expected here is from the children to parent tables. For example,&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'when we have two tables, ParentTable and ChildTable, the dataTables&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'array must contain then in this order ChildTable, ParentTable with the&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'same order for their data adapters in dataAdapters array.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'The whole operation must be executed in a transactional scope so that we can undone&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'partial operations if an error occurs.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; transaction &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("No transaction supplied")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Input parameter checks&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; dataAdapters &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("No dataAdapters supplied")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; dataTables &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Nothing&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("No dataTables supplied")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; dataAdapters.Length = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("No dataAdapters supplied")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; dataAdapters.Length &amp;lt;&amp;gt; dataTables.Length &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("Number of dataAdapters does not much number of dataTables")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Holds the number of the tables or data adapters&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = dataAdapters.Length&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Check that all tables supplied belong to the same dataset&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; DataSet &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataSet = dataTables(0).DataSet&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Not&lt;/font&gt;&lt;font size="2"&gt; dataTables(ObjectCounter).DataSet &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Is&lt;/font&gt;&lt;font size="2"&gt; DataSet &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("Found tables that belong to different dataSets")&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Try&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Set all commands object to use the existing transaction and connection&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).UpdateCommand.Connection = transaction.Connection&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).UpdateCommand.Transaction = transaction&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).InsertCommand.Connection = transaction.Connection&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).InsertCommand.Transaction = transaction&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).DeleteCommand.Connection = transaction.Connection&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).DeleteCommand.Transaction = transaction&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'This array will hold the initialy desired action for each row&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; TableRows(NumberOfObjects) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; TableRows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Initialize the TableRows object for all tables&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;TableRows(ObjectCounter) = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; TableRows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Store the rows that need to be inserted to the database&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;TableRows(ObjectCounter).InsertedRows = dataTables(ObjectCounter).Select("", "", DataViewRowState.Added)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Store the rows that need to be updated to the database&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;TableRows(ObjectCounter).UpdatedRows = dataTables(ObjectCounter).Select("", "", DataViewRowState.ModifiedCurrent)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Store the rows that need to be deleted to the database&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;TableRows(ObjectCounter).DeletedRows = GetDeletedRows(dataTables(ObjectCounter))&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Add handlers to row update events to handle properly the modification of&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'rows in insert and delete operations.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; NumberOfObjects - 1&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AddHandler&lt;/font&gt;&lt;font size="2"&gt; dataAdapters(ObjectCounter).RowUpdated, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AddressOf&lt;/font&gt;&lt;font size="2"&gt; RowUpdated&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Now we are ready to actualy update the database. The actions must be taken in&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'this order: delete, insert, update.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'See: http://blogs.gr/equilibrium/archive/2005/07/16/Order_of_Insert_Update_Delete_with_DataSet_Updates.aspx&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Process deleted rows (starting from children tables to parent tables)&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = NumberOfObjects - 1 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Step&lt;/font&gt;&lt;font size="2"&gt; -1&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).Update(TableRows(ObjectCounter).DeletedRows)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Close constrains of the dataset because we will manualy handle the changes in identity columns&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;dataTables(0).DataSet.EnforceConstraints = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;False&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Process inserted rows (from parent tables to children tables) &lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = NumberOfObjects - 1 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Step&lt;/font&gt;&lt;font size="2"&gt; -1&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).Update(TableRows(ObjectCounter).InsertedRows)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Open dataset constrains&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;dataTables(0).DataSet.EnforceConstraints = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;True&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Process modified rows (from parent tables to children tables) &lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;For&lt;/font&gt;&lt;font size="2"&gt; ObjectCounter &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Integer&lt;/font&gt;&lt;font size="2"&gt; = NumberOfObjects - 1 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;To&lt;/font&gt;&lt;font size="2"&gt; 0 &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Step&lt;/font&gt;&lt;font size="2"&gt; -1&lt;/p&gt; &lt;p&gt;dataAdapters(ObjectCounter).Update(TableRows(ObjectCounter).UpdatedRows)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Next&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Catch&lt;/font&gt;&lt;font size="2"&gt; ex &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; Exception&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("Failed to update DataSet over transaction", ex)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Try&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Return&lt;/font&gt;&lt;font size="2"&gt; DataSet&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/font&gt;&lt;font size="2"&gt; UpdateDataSet(&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; dataAdapters() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SqlDataAdapter, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; dataTables() &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataTable, &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;ByVal&lt;/font&gt;&lt;font size="2"&gt; connection &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SqlConnection) &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataSet&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Calls the UpdateDataSet function by first creating a transaction based&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'on an existing connection.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; CloseConnectionOnReturn &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Boolean&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; connection.State = ConnectionState.Open &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;CloseConnectionOnReturn = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;False&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Else&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;CloseConnectionOnReturn = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;True&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;connection.Open()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Dim&lt;/font&gt;&lt;font size="2"&gt; Transaction &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; SqlClient.SqlTransaction&lt;/p&gt; &lt;p&gt;Transaction = connection.BeginTransaction&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Try&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;UpdateDataSet = UpdateDataSet(dataAdapters, dataTables, Transaction)&lt;/p&gt; &lt;p&gt;Transaction.Commit()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Catch&lt;/font&gt;&lt;font size="2"&gt; ex &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; Exception&lt;/p&gt; &lt;p&gt;Transaction.Rollback()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Throw&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;New&lt;/font&gt;&lt;font size="2"&gt; Exception("Failed to update DataSet", ex)&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Finally&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/font&gt;&lt;font size="2"&gt; CloseConnectionOnReturn &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;AndAlso&lt;/font&gt;&lt;font size="2"&gt; connection.State = ConnectionState.Open &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Then&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;connection.Close()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;If&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Try&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Function&lt;/p&gt; &lt;p&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Module&lt;/p&gt; &lt;p&gt;Public&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/font&gt;&lt;font size="2"&gt; TableRows&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'This class will hold the initialy inserted, updated and deleted rows of each table.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'Because we manualy control the row states of all table rows, we can rely on row states&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#008000" size="2"&gt;'so we have to store here the originaly desired action on each row.&lt;/p&gt;&lt;/font&gt;&lt;font size="2"&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; InsertedRows &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; UpdatedRows &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow()&lt;/p&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Public&lt;/font&gt;&lt;font size="2"&gt; DeletedRows &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;As&lt;/font&gt;&lt;font size="2"&gt; DataRow()&lt;/p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt; &lt;p&gt;End&lt;/font&gt;&lt;font size="2"&gt; &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;Class&lt;/font&gt;&lt;/p&gt;&lt;/font&gt;&lt;br&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/pre&gt; &lt;p&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;&amp;nbsp;&lt;/p&gt;&lt;/font&gt;</description></item></channel></rss>