<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/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>Beth Massi - Sharing the goodness that is VB : Article, Winforms</title><link>http://blogs.msdn.com/bethmassi/archive/tags/Article/Winforms/default.aspx</link><description>Tags: Article, Winforms</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Using TableAdapters to Insert Related Data into an MS Access Database</title><link>http://blogs.msdn.com/bethmassi/archive/2009/05/14/using-tableadapters-to-insert-related-data-into-an-ms-access-database.aspx</link><pubDate>Fri, 15 May 2009 03:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9617454</guid><dc:creator>Beth Massi</dc:creator><slash:comments>43</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/9617454.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=9617454</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=9617454</wfw:comment><description>&lt;P&gt;I’ve &lt;A href="http://blogs.msdn.com/bethmassi/archive/2007/07/10/working-with-tableadapters-related-datatables-and-transactions.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/07/10/working-with-tableadapters-related-datatables-and-transactions.aspx"&gt;posted before&lt;/A&gt; about how to use TableAdapters to update parent-child (master-detail) relationships against SQL server. It’s pretty straightforward and Visual Studio generates all the code for you to properly insert, update and delete your data. However if you’re using MS Access then there’s one thing that Visual Studio doesn’t do because it’s not supported when using Access.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;How Parent-Child Inserts Work&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;When Visual Studio generates the insert commands on a SQL-Server TableAdapter it looks to see if the table’s primary keys are auto-generated (identity columns) and if so, Visual Studio will write an additional statement to retrieve the key using the &lt;A href="http://msdn.microsoft.com/en-us/library/ms190315.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/ms190315.aspx"&gt;SCOPE_IDENTITY&lt;/A&gt; functionality of SQL Server. When in the DataSet designer, if you look at the insert statement in the properties window for the SQLTableAdapter you will see &lt;STRONG&gt;two &lt;/STRONG&gt;statements separated by a semi-colon:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_2.png" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_2.png"&gt;&lt;IMG title=image style="BORDER-TOP-WIDTH: 0px; DISPLAY: inline; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=352 alt=image src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb.png" width=571 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;INSERT INTO [dbo].[Products] ([ProductName], [SupplierID], [CategoryID], [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued]) VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice, @UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued&lt;/FONT&gt;&lt;STRONG&gt;&lt;FONT face="Courier New" size=2&gt;); &lt;BR&gt;SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued FROM Products WHERE (ProductID = SCOPE_IDENTITY())&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;SQL Server supports batch statements through ADO.NET commands so this will populate the primary key back in the DataRow inside the DataSet as each row is inserted into the database. If you are enforcing foreign key constraints with a parent-child relation set up on two DataTables and you set the Update Rule to Cascade then any foreign key references will also be updated in the children. Because the TableAdapterManager will save the children after their parent records, when the child saves to the database it will contain the correct parent key which must already exist in the database before a child can be inserted in order to maintain referential integrity in the database. &lt;/P&gt;
&lt;P&gt;Unfortunately Access doesn’t support batch statements. If you look at what is generated for Access you will only see one statement (also the OLEDB provider does not support named parameters hence the question mark placeholders):&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;INSERT INTO `Products` (`ProductName`, `SupplierID`, `CategoryID`, `QuantityPerUnit`, `UnitPrice`, `UnitsInStock`, `UnitsOnOrder`, `ReorderLevel`, `Discontinued`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;So if you are doing inserts, especially for related parent-child data, you need a way to intercept the DataRow and set the primary key right &lt;STRONG&gt;after &lt;/STRONG&gt;the row is inserted into the database and &lt;STRONG&gt;before &lt;/STRONG&gt;any children are inserted. There’s an &lt;A href="http://msdn.microsoft.com/en-us/library/ms971502.aspx#manidcrisis_topic2" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/ms971502.aspx#manidcrisis_topic2"&gt;excellent article by Bill Vaughn (VB MVP) that explains this issue&lt;/A&gt; as well as &lt;A href="http://support.microsoft.com/kb/815629" target=_blank mce_href="http://support.microsoft.com/kb/815629"&gt;a KB Article&lt;/A&gt; that shows how to solve it using the DataAdapter. These were written before Visual Studio had the concept of TableAdapters (which were added in VS 2008) so let’s see how we could use this technique to enhance our TableAdapters via partial classes.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Setting up the Parent-Child DataSet&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The first step is to make sure you set up the tables in your Access database to use the AutoNumber feature for the primary keys on the rows. Here I’m using Access 2007 against the Northwind Access database. AutoNumber is used for both the primary keys on the Products and Categories tables:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_4.png" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_4.png"&gt;&lt;IMG title=image style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; DISPLAY: inline; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=398 alt=image src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_1.png" width=508 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_1.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;Next you need to make sure you set up the relationship on the DataSet properly so that the primary key on the parent will cascade to the foreign key on the child. Set the relation in the DataSet designer to "Both Relation and Foreign Key Constraint" and then set the Update and Delete rules to Cascade. Just right-click on the relation and select "Edit Relation" in the DataSet designer:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_6.png" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_6.png"&gt;&lt;IMG title=image style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; DISPLAY: inline; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=244 alt=image src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_2.png" width=410 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_2.png"&gt;&lt;/A&gt;&amp;nbsp;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_8.png" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_8.png"&gt;&lt;IMG title=image style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; DISPLAY: inline; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=491 alt=image src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_3.png" width=512 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_3.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Loading and Editing the Parent-Child DataSet&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;You now are telling the DataSet to enforce the foreign key relationship which means that you &lt;STRONG&gt;must&lt;/STRONG&gt; have a parent for every child. This means you have to load the data in parent then child order. You also have to be careful with your queries. You have to &lt;STRONG&gt;make sure that every row in the child DataTable will have a corresponding parent row in the parent DataTable&lt;/STRONG&gt;. This also means that you have to make sure to call EndEdit on any new parent BindingSource before any children can be added. &lt;/P&gt;
&lt;P&gt;For example, from the Data Sources window drag the Categories parent table as details and the related child Products table as a DataGridView on the form and Visual Studio will generate the code to load and save our data. &lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_10.png" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_10.png"&gt;&lt;IMG title=image style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; DISPLAY: inline; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=443 alt=image src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_4.png" width=677 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/UsingTableAdapterstoInsertRelatedDataint_D5FF/image_thumb_4.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;Head over to the code behind and make sure that the parent is filled first before the child. Also make sure that EndEdit is called on the CategoriesBindingSource &lt;STRONG&gt;before &lt;/STRONG&gt;a new product can be inserted into the DataGridView. EndEdit will flush the data row being edited by the controls into the DataTable. In this example I just am calling EndEdit on the CategoriesBindingSource when the user selects the grid. &lt;/P&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Public Class &lt;/SPAN&gt;Form1

    &lt;SPAN style="COLOR: blue"&gt;Private Sub &lt;/SPAN&gt;CategoriesBindingNavigatorSaveItem_Click() _
            &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;CategoriesBindingNavigatorSaveItem.Click
        &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.Validate()
        &lt;SPAN style="COLOR: green"&gt;'Call EndEdit on all BindingSources! 
        &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.CategoriesBindingSource.EndEdit()
        &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.EndEdit()
        &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.TableAdapterManager.UpdateAll(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsDataSet)
    &lt;SPAN style="COLOR: blue"&gt;End Sub

    Private Sub &lt;/SPAN&gt;Form1_Load() &lt;SPAN style="COLOR: blue"&gt;Handles MyBase&lt;/SPAN&gt;.Load
        &lt;SPAN style="COLOR: green"&gt;'Load parent before child!
        &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.CategoriesTableAdapter.Fill(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsDataSet.Categories)
        &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsTableAdapter.Fill(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsDataSet.Products)
    &lt;SPAN style="COLOR: blue"&gt;End Sub

    Private Sub &lt;/SPAN&gt;ProductsDataGridView_Enter() &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;ProductsDataGridView.Enter
        &lt;SPAN style="COLOR: green"&gt;'You must commit the parent row to the DataTable before adding child rows 
        &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.CategoriesBindingSource.EndEdit()
    &lt;SPAN style="COLOR: blue"&gt;End Sub

End Class&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Note that anytime you call EndEdit and flush the data to the DataTable, the row must not fail any constraints either (i.e. if NULLs aren’t being allowed then you have to set those values). One way to handle this is to add code to set default values in the &lt;A href="http://msdn.microsoft.com/en-us/library/system.data.datatable.tablenewrow.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/system.data.datatable.tablenewrow.aspx"&gt;TableNewRow&lt;/A&gt; handler on the DataTable. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Enhancing the TableAdapter Partial Classes&lt;/STRONG&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Now for the good stuff. Like I mentioned in the beginning, you need a way to set the primary key on the parent right after the row is inserted into the database and before any children are inserted. Now that we have keys cascading we just need to write code to handle the RowUpdated event on the DataAdapter inside the TableAdapter partial class. TableAdapters are generated classes that Visual Studio creates for us from the DataSet designer. These classes are declared as &lt;A href="http://msdn.microsoft.com/en-us/library/yfzd5350.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/yfzd5350.aspx"&gt;Partial Classes&lt;/A&gt; so that means we can add code to the same class even if it’s in a separate file. Right-click on the TableAdapter class in the DataSet Designer and select View Code and the partial class file that you can edit will be created for you. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Namespace &lt;/SPAN&gt;ProductsDataSetTableAdapters

    &lt;SPAN style="COLOR: blue"&gt;Partial Public Class &lt;/SPAN&gt;CategoriesTableAdapter

    &lt;SPAN style="COLOR: blue"&gt;End Class

    Partial Public Class &lt;/SPAN&gt;ProductsTableAdapter

    &lt;SPAN style="COLOR: blue"&gt;End Class
End Namespace&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;In these classes we can handle the RowUpdated event on the private variable _adapter which gives us access to the ADO.NET DataAdapter that is executing the updates to our rows. The way we retrieve the primary key is by executing the statement&amp;nbsp; SELECT @@IDENTITY which tells Access to send back the last primary key it used on the connection. Because you have to add this handler to all your TableAdapters that are working against MS Access, to make things more manageable you can create a class with a Shared (static) method to handle setting the key and then call that from the handlers.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Imports &lt;/SPAN&gt;System.Data.OleDb

&lt;SPAN style="COLOR: blue"&gt;Public Class &lt;/SPAN&gt;AccessIDHelper
    &lt;SPAN style="COLOR: green"&gt;''' &amp;lt;summary&amp;gt;
    ''' Retrieves the primary key autonumber values from Access
    ''' &amp;lt;/summary&amp;gt;
    ''' &amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
    &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Public Shared Sub &lt;/SPAN&gt;SetPrimaryKey(&lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;trans &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;OleDbTransaction, _
                                    &lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;e &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;OleDbRowUpdatedEventArgs)
        &lt;SPAN style="COLOR: blue"&gt;If &lt;/SPAN&gt;e.Status = UpdateStatus.Continue &lt;SPAN style="COLOR: blue"&gt;AndAlso &lt;/SPAN&gt;_
           e.StatementType = StatementType.Insert &lt;SPAN style="COLOR: blue"&gt;Then
            &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;' If this is an INSERT operation...
            &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim &lt;/SPAN&gt;pk = e.Row.Table.PrimaryKey
            &lt;SPAN style="COLOR: green"&gt;' and a primary key column exists...
            &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;If &lt;/SPAN&gt;pk &lt;SPAN style="COLOR: blue"&gt;IsNot Nothing AndAlso &lt;/SPAN&gt;pk.Count = 1 &lt;SPAN style="COLOR: blue"&gt;Then
                Dim &lt;/SPAN&gt;cmdGetIdentity &lt;SPAN style="COLOR: blue"&gt;As New &lt;/SPAN&gt;OleDbCommand(&lt;SPAN style="COLOR: #a31515"&gt;"SELECT @@IDENTITY"&lt;/SPAN&gt;, trans.Connection, trans)
                &lt;SPAN style="COLOR: green"&gt;' Execute the post-update query to fetch new @@Identity
                &lt;/SPAN&gt;e.Row(pk(0)) = &lt;SPAN style="COLOR: blue"&gt;CInt&lt;/SPAN&gt;(cmdGetIdentity.ExecuteScalar)
                e.Row.AcceptChanges()
            &lt;SPAN style="COLOR: blue"&gt;End If
        End If
    End Sub
End Class

Namespace &lt;/SPAN&gt;ProductsDataSetTableAdapters

    &lt;SPAN style="COLOR: blue"&gt;Partial Public Class &lt;/SPAN&gt;CategoriesTableAdapter

        &lt;SPAN style="COLOR: blue"&gt;Private Sub &lt;/SPAN&gt;_adapter_RowUpdated(&lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;sender &lt;SPAN style="COLOR: blue"&gt;As Object&lt;/SPAN&gt;, _
                                        &lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;e &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;System.Data.OleDb.OleDbRowUpdatedEventArgs) _
                                        &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;_adapter.RowUpdated

            AccessIDHelper.SetPrimaryKey(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.Transaction, e)
        &lt;SPAN style="COLOR: blue"&gt;End Sub
    End Class

    Partial Public Class &lt;/SPAN&gt;ProductsTableAdapter

        &lt;SPAN style="COLOR: blue"&gt;Private Sub &lt;/SPAN&gt;_adapter_RowUpdated(&lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;sender &lt;SPAN style="COLOR: blue"&gt;As Object&lt;/SPAN&gt;, _
                                        &lt;SPAN style="COLOR: blue"&gt;ByVal &lt;/SPAN&gt;e &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;System.Data.OleDb.OleDbRowUpdatedEventArgs) _
                                        &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;_adapter.RowUpdated

            AccessIDHelper.SetPrimaryKey(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.Transaction, e)
        &lt;SPAN style="COLOR: blue"&gt;End Sub
    End Class
End Namespace&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;So that’s how you can get the primary keys into the data rows and have them properly cascaded to the child rows. So now when the children are updated they will have the correct foreign key and the parent will exist in the database. I hope this helps clear up how to work with Access and Visual Studio.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;I’ve &lt;A href="http://code.msdn.microsoft.com/AccessDataVS/" target=_blank mce_href="http://code.msdn.microsoft.com/AccessDataVS/"&gt;posted this example on CodeGallery&lt;/A&gt; so have a look.&lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9617454" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Tally Rows in a DataSet that Match a Condition</title><link>http://blogs.msdn.com/bethmassi/archive/2009/04/27/tally-rows-in-a-dataset-that-match-a-condition.aspx</link><pubDate>Tue, 28 Apr 2009 05:24:21 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9572773</guid><dc:creator>Beth Massi</dc:creator><slash:comments>8</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/9572773.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=9572773</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=9572773</wfw:comment><description>&lt;p&gt;Today I got a question that comes up often in data application programming about how to count rows in a DataSet that matched a condition. The DataSet may be bound to a DataGridView or other list control and it’s tempting to start looking at the control to see if you can coax it into returning what you need but usually there is a much better way.&lt;/p&gt;  &lt;p&gt;For instance, say we have a table in our database called “Inbox” that has varchar fields Subject, From, and Status and we’d like to tally all the rows where the Status = “Unread”. Suppose we’ve also created a typed DataSet that contains this Inbox table. You create a data-bound Windows Form with a DataGridView on it by dragging the table from the Data sources window onto the form (&lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb725824.aspx"&gt;like I showed in this video&lt;/a&gt;). When you do this, Visual Studio generates code that hooks up your DataGridView’s DataSource property to a BindingSource object which in turn has it’s DataSource set to your DataSet. This is a good thing. The BindingSource is a simple controller that provides currency between your DataSet and the DataGidView UI. (In WPF this is similar to the CollectionView object). You write code against the BindingSource instead so that it doesn’t matter what kind of control is being used to display the data.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;BindingSource to the Rescue&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The BindingSource in this case is actually working with a DataView, not the actual DataTable, which may seem confusing. This is needed for related data binding to work (see &lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb643827.aspx"&gt;this video&lt;/a&gt; and &lt;a href="http://msdn.microsoft.com/en-us/vbasic/cc138241.aspx"&gt;this one&lt;/a&gt;). Since the BindingSource manages the currency (current row position) that is being displayed by the controls, in this example the BindingSource.List will return the DataView and the BindingSource.Current property will always return the DataRowView. You access your typed DataRow by casting the DataRowView.Row property. So to get a count of items in the DataView we could just simply ask the BindingSource for a count of it’s rows no matter what kind of control is being used for the display:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count = &lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.Count&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Or we could grab the DataView and ask for its count:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;dv = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.List, DataView)
&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count = dv.Count&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Once you have the DataView you can apply more filtering on it directly or we can loop through it to tally the rows where Status = “Unread”. &lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;dv = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.List, DataView)
&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count = 0
&lt;span style="color: blue"&gt;For Each &lt;/span&gt;drv &lt;span style="color: blue"&gt;As &lt;/span&gt;DataRowView &lt;span style="color: blue"&gt;In &lt;/span&gt;dv
    &lt;span style="color: blue"&gt;Dim &lt;/span&gt;inboxRow = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(drv.Row, EmailDataSet.InboxRow)
    &lt;span style="color: blue"&gt;If &lt;/span&gt;inboxRow.Status = &lt;span style="color: #a31515"&gt;&amp;quot;Unread&amp;quot; &lt;/span&gt;&lt;span style="color: blue"&gt;Then
        &lt;/span&gt;count += 1
    &lt;span style="color: blue"&gt;End If
Next&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;But the neat thing about using the BindingSource is that you always know the current row being displayed. So if we want to tally the rows based on a condition in the current row we go through the BindingSource. You can easily write the code that gets the current row as your typed data row by using a code snippet. Right-click in the editor, select Data – LINQ, XML, Designer, ADO.NET &amp;gt; Designer Features and ADO.NET &amp;gt; Converts BindingSource.Current to a specific row in a DataTable:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/TallyRowsinaDataSetthatMatchaCondition_110E1/image_4.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="211" alt="image" src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/TallyRowsinaDataSetthatMatchaCondition_110E1/image_thumb_1.png" width="709" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;This code returns a tally of rows in the current view that have the same status as the selected row. Instead of looping through the data manually with For Each, this code sets the Filter property of the DataView instead:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;currentRow &lt;span style="color: blue"&gt;As &lt;/span&gt;EmailDataSet.InboxRow
currentRow = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.Current, DataRowView).Row, EmailDataSet.InboxRow)

&lt;span style="color: blue"&gt;Dim &lt;/span&gt;dv = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.List, DataView)
dv.RowFilter = &lt;span style="color: #a31515"&gt;&amp;quot;Status ='&amp;quot; &lt;/span&gt;&amp;amp; currentRow.Status &amp;amp; &lt;span style="color: #a31515"&gt;&amp;quot;'&amp;quot;
&lt;/span&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count = dv.Count&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;This technique causes any bound controls to update to the new filter. To remove the filter, set the Filter property to Nothing or the empty string. Although this code may work for a lot of scenarios, we may have a situation where we do not want to affect any of the bound controls by changing the filter. And even though this is a simple condition it would be nice not to have to manually write the loop ourselves. This is a perfect place to use LINQ instead. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LINQ to the Rescue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If we want to tally the entire DataTable for rows matching a condition we can use LINQ to DataSets with Visual Basic’s Aggregate clause. This doesn’t involve a BindingSource at all but be aware that the DataView that a BindingSource may be displaying data from may be filtered. In the case of this example the DataView contains the same rows as the DataTable (no filter) so we can write a simple Aggregate query to count the rows:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count &lt;span style="color: blue"&gt;As Integer &lt;/span&gt;= &lt;span style="color: blue"&gt;Aggregate &lt;/span&gt;row &lt;span style="color: blue"&gt;In Me&lt;/span&gt;.EmailDataSet.Inbox _
                       &lt;span style="color: blue"&gt;Where &lt;/span&gt;row.Status = &lt;span style="color: #a31515"&gt;&amp;quot;Unread&amp;quot; &lt;/span&gt;_
                       &lt;span style="color: blue"&gt;Into &lt;/span&gt;UnreadCount = Count()&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;This will return an integer indicating the number of rows in the entire DataTable that matched the Where clause. But what if we do want to use the BindingSource and take into account any filters that are being applied to the DataView in which controls are bound? In this case we can still use an Aggregate LINQ query it will just be against the DataView so we will need to cast a few things:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Dim &lt;/span&gt;dv = &lt;span style="color: blue"&gt;CType&lt;/span&gt;(&lt;span style="color: blue"&gt;Me&lt;/span&gt;.InboxBindingSource.List, DataView)
&lt;span style="color: blue"&gt;Dim &lt;/span&gt;count &lt;span style="color: blue"&gt;As Integer &lt;/span&gt;= &lt;span style="color: blue"&gt;Aggregate &lt;/span&gt;row &lt;span style="color: blue"&gt;In &lt;/span&gt;dv.Cast(&lt;span style="color: blue"&gt;Of &lt;/span&gt;DataRowView).AsQueryable _
                       &lt;span style="color: blue"&gt;Where CType&lt;/span&gt;(row.Row, EmailDataSet.InboxRow).Status = &lt;span style="color: #a31515"&gt;&amp;quot;Unread&amp;quot; &lt;/span&gt;_
                       &lt;span style="color: blue"&gt;Into &lt;/span&gt;UnreadCount = Count()&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Now what would be really nice is to automatically tally this information from the DataSet and display it in a label on the Form anytime the user made any changes to the rows. We can do this easily by handling the BindingSource’s ListChanged event and putting our Aggregate LINQ query in there:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;Private Sub &lt;/span&gt;InboxBindingSource_ListChanged() &lt;span style="color: blue"&gt;Handles &lt;/span&gt;InboxBindingSource.ListChanged
    &lt;span style="color: blue"&gt;Dim &lt;/span&gt;count &lt;span style="color: blue"&gt;As Integer &lt;/span&gt;= &lt;span style="color: blue"&gt;Aggregate &lt;/span&gt;row &lt;span style="color: blue"&gt;In Me&lt;/span&gt;.EmailDataSet.Inbox _
                           &lt;span style="color: blue"&gt;Where &lt;/span&gt;row.Status = &lt;span style="color: #a31515"&gt;&amp;quot;Unread&amp;quot; &lt;/span&gt;_
                           &lt;span style="color: blue"&gt;Into &lt;/span&gt;UnreadCount = Count()

    &lt;span style="color: blue"&gt;Me&lt;/span&gt;.lblStatus.Text = count.ToString() &amp;amp; &lt;span style="color: #a31515"&gt;&amp;quot; unread email.&amp;quot;
&lt;/span&gt;&lt;span style="color: blue"&gt;End Sub&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Now this will give us a “live” running tally and we’re not bound to the UI controls at all. Nice. For more information on Aggregate queries and DataSets &lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb737877.aspx"&gt;check out this video&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;More resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb531251.aspx"&gt;Visual Basic Aggregate Clause&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb688088.aspx"&gt;101 Visual Basic LINQ Samples&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb737877.aspx"&gt;How Do I: LINQ over DataSets?&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/vbasic/bb725824.aspx"&gt;How Do I: Understand Data?&lt;/a&gt;&lt;/li&gt;

  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/vbasic/cc138241.aspx"&gt;How Do I: Update Related Tables?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9572773" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Editing Data from Two Tables in a Single DataGridView</title><link>http://blogs.msdn.com/bethmassi/archive/2008/10/15/editing-data-from-two-tables-in-a-single-datagridview.aspx</link><pubDate>Thu, 16 Oct 2008 04:31:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9001289</guid><dc:creator>Beth Massi</dc:creator><slash:comments>24</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/9001289.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=9001289</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=9001289</wfw:comment><description>&lt;P&gt;I've had a lot of questions lately on how to display data from two separate tables in the database into a single DataGridView for editing. It sure would be nice if all our data was in a single table, but in reality most of the time it's not. Basically the problem is that we want one single table (entity) representation on the client even though we have two physical tables in the database holding the information... thus we need to "split" the data in our entity on the client side into two or more physical tables on the server. There are many ways you can do this depending on the relations in the database and also depending on what your client-side data source happens to be. I'll present a simple, common database table scenario and then attempt to explain how we can work with it using DataTables, LINQ to SQL classes, and then an Entity Data Model --- three different approaches to working with data in Visual Studio.&lt;/P&gt;
&lt;P&gt;So let's take a very simple example. In my database I have two tables with a one-to-one relationship, Customer and CustomerContactInfo, one storing basic information about a customer and another that stores contact information:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity1_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity1_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=244 alt=entity1 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity1_thumb.jpg" width=522 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity1_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Entity Splitting DataTables&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If we're using DataSets in our application already we probably want to represent this as a single Customer client-side DataTable. To do this, we need to specify some stored procedures in our database for Select, Insert, Update, and Delete so that when ADO.NET retrieves our data or sends back the updated, inserted and deleted rows to the database, it calls our stored procedures that do the work of splitting the data into the proper physical tables. You can easily map DataTables in the DataSet designer to stored procedures. In our example these stored procedures are going to be very simple:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;CREATE PROCEDURE &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[GetCustomers] &lt;SPAN style="COLOR: blue"&gt;AS
SELECT  &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;CustomerID&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;Title&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;FirstName&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;MiddleName&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;LastName&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;Suffix&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;CompanyName&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;SalesPerson&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;contact&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;EmailAddress&lt;SPAN style="COLOR: gray"&gt;, 
        &lt;/SPAN&gt;contact&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;Phone
&lt;SPAN style="COLOR: blue"&gt;FROM    &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[Customer] &lt;SPAN style="COLOR: blue"&gt;AS &lt;/SPAN&gt;cust
&lt;SPAN style="COLOR: gray"&gt;JOIN    &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[CustomerContactInfo] 
        &lt;SPAN style="COLOR: blue"&gt;AS &lt;/SPAN&gt;contact &lt;SPAN style="COLOR: blue"&gt;ON &lt;/SPAN&gt;cust&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;CustomerID &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;contact&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;CustomerID&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;CREATE PROCEDURE &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[DeleteCustomer]&lt;SPAN style="COLOR: gray"&gt;(
    &lt;/SPAN&gt;@CustomerID [int]
&lt;SPAN style="COLOR: gray"&gt;) &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;AS
BEGIN

    DELETE   &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[CustomerContactInfo]
    &lt;SPAN style="COLOR: blue"&gt;WHERE    &lt;/SPAN&gt;[CustomerID] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@CustomerID
    
    &lt;SPAN style="COLOR: blue"&gt;DELETE   &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[Customer]
    &lt;SPAN style="COLOR: blue"&gt;WHERE    &lt;/SPAN&gt;[CustomerID] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@CustomerID
    
&lt;SPAN style="COLOR: blue"&gt;END&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;CREATE PROCEDURE &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[UpdateCustomer]&lt;SPAN style="COLOR: gray"&gt;(
    &lt;/SPAN&gt;@CustomerID [int]&lt;SPAN style="COLOR: gray"&gt;,
    &lt;/SPAN&gt;@Title [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;8&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@FirstName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@MiddleName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@LastName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@Suffix [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;10&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@CompanyName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;128&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@SalesPerson [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;256&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@EmailAddress [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@Phone [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;25&lt;SPAN style="COLOR: gray"&gt;)
) &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;AS
BEGIN

    UPDATE  &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[Customer]
    &lt;SPAN style="COLOR: blue"&gt;SET     &lt;/SPAN&gt;[Title] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@Title&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[FirstName] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@FirstName&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[MiddleName] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@MiddleName&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[LastName] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@LastName&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[Suffix] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@Suffix&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[CompanyName] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@CompanyName&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[SalesPerson] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@SalesPerson
    &lt;SPAN style="COLOR: blue"&gt;WHERE   &lt;/SPAN&gt;[CustomerID] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@CustomerID

    &lt;SPAN style="COLOR: blue"&gt;UPDATE  &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[CustomerContactInfo]
    &lt;SPAN style="COLOR: blue"&gt;SET     &lt;/SPAN&gt;[EmailAddress] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@EmailAddress&lt;SPAN style="COLOR: gray"&gt;,
            &lt;/SPAN&gt;[Phone] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@Phone
    &lt;SPAN style="COLOR: blue"&gt;WHERE   &lt;/SPAN&gt;[CustomerID] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;@CustomerID
    
&lt;SPAN style="COLOR: blue"&gt;END&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;CREATE PROCEDURE &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[InsertCustomer]&lt;SPAN style="COLOR: gray"&gt;(
    &lt;/SPAN&gt;@Title [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;8&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@FirstName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@MiddleName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@LastName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@Suffix [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;10&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@CompanyName [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;128&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@SalesPerson [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;256&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@EmailAddress [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;50&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@Phone [nvarchar]&lt;SPAN style="COLOR: gray"&gt;(&lt;/SPAN&gt;25&lt;SPAN style="COLOR: gray"&gt;),
    &lt;/SPAN&gt;@CustomerID &lt;SPAN style="COLOR: blue"&gt;int OUTPUT
&lt;/SPAN&gt;&lt;SPAN style="COLOR: gray"&gt;) &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;AS
BEGIN

    INSERT INTO &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[Customer]
    &lt;SPAN style="COLOR: blue"&gt;VALUES &lt;/SPAN&gt;&lt;SPAN style="COLOR: gray"&gt;(
        &lt;/SPAN&gt;@Title&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@FirstName&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@MiddleName&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@LastName&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@Suffix&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@CompanyName&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@SalesPerson &lt;SPAN style="COLOR: gray"&gt;)
    
    &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;-- Get back the customer ID 
    &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;SELECT &lt;/SPAN&gt;@customerID &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;CustomerID
    &lt;SPAN style="COLOR: blue"&gt;FROM &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[Customer]
    &lt;SPAN style="COLOR: blue"&gt;WHERE &lt;/SPAN&gt;&lt;SPAN style="COLOR: magenta"&gt;@@ROWCOUNT &lt;/SPAN&gt;&lt;SPAN style="COLOR: gray"&gt;&amp;gt; &lt;/SPAN&gt;0 &lt;SPAN style="COLOR: gray"&gt;AND &lt;/SPAN&gt;[CustomerID] &lt;SPAN style="COLOR: gray"&gt;= &lt;/SPAN&gt;&lt;SPAN style="COLOR: magenta"&gt;scope_identity&lt;/SPAN&gt;&lt;SPAN style="COLOR: gray"&gt;()

    &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;INSERT INTO &lt;/SPAN&gt;[dbo]&lt;SPAN style="COLOR: gray"&gt;.&lt;/SPAN&gt;[CustomerContactInfo]
    &lt;SPAN style="COLOR: blue"&gt;VALUES &lt;/SPAN&gt;&lt;SPAN style="COLOR: gray"&gt;(
        &lt;/SPAN&gt;@customerID&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@EmailAddress&lt;SPAN style="COLOR: gray"&gt;,
        &lt;/SPAN&gt;@Phone&lt;SPAN style="COLOR: gray"&gt;)
    
&lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;END&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Now that we have that set up, open up the Data Source Window in Visual Studio and add a new data source, select the Database and then select just the GetCustomers stored procedure:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity2_4.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity2_4.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=480 alt=entity2 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity2_thumb_1.jpg" width=625 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity2_thumb_1.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;Click Finish and then open up the DataSet designer by double-clicking on the CustomerDataset.xsd in the Solution Explorer. You'll notice that the name of the DataTable is GetCustomers so change that to just "Customer". Next we need to configure the DataTable so that it will use our stored procedures so right click on the DataTable and select "Configure...". The TableAdapter Configuration Wizard should open and this will allow you to map your stored procedures to the Update, Insert and Delete commands. By default the fields will match up by name so you shouldn't have to do anything but drop down the combo boxes and select the right procedures for each action.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity3_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity3_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=480 alt=entity3 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity3_thumb.jpg" width=624 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity3_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;To test this out you can drag the Customer table from the Data Sources window on to a Windows Form as a DataGrid and immediately run it. &lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity4_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity4_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=378 alt=entity4 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity4_thumb.jpg" width=640 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity4_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;You will see the data from both tables displayed in the grid and they will be editable. As you update, insert and delete rows here, the DataSet will keep track of these changes for you. So when it is time to save the data via a call to the TableAdapterManager.UpdateAll method, the proper stored procedure will be called for each row that was modified. &lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity5_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity5_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=189 alt=entity5 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity5_thumb.jpg" width=640 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity5_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Entity Splitting LINQ to SQL Classes&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;So with a Dataset, the DataTables do not have to map one-to-one with your database tables, but you need to specify how the data should be saved back to the database via stored procedures. This is also true if using LINQ to SQL classes. In that case though, you drag methods onto the method pane &lt;STRONG&gt;first&lt;/STRONG&gt; and then map them to the class in the designer by right-clicking on the class and selecting "Configure Behavior...". &lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity6_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity6_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=480 alt=entity6 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity6_thumb.jpg" width=639 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity6_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;However with LINQ to SQL classes you can't map the Select behavior unfortunately. What you do instead is map the result of the GetCustomers method to a result type of Customer (the class we created on the design surface). &lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity7_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity7_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; MARGIN: 5px 10px 0px 0px; BORDER-RIGHT-WIDTH: 0px" height=182 alt=entity7 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity7_thumb.jpg" width=240 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity7_thumb.jpg"&gt;&lt;/A&gt;Then when accessing the customers data you need to remember to call the GetCustomers method and not access the Customers directly in the DataContext, otherwise you'll get a SQLException that it cant find the additional columns when retrieving the data (in our case EmailAddress and Phone). &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Private &lt;/SPAN&gt;db &lt;SPAN style="COLOR: blue"&gt;As New &lt;/SPAN&gt;MyDataContext

&lt;SPAN style="COLOR: blue"&gt;Private Sub&lt;/SPAN&gt;Form1_Load() &lt;SPAN style="COLOR: blue"&gt;Handles MyBase&lt;/SPAN&gt;.Load&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&lt;SPAN style="COLOR: green"&gt;    'This will properly populate a collection of our&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;    ' Customer entities.&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;    &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.CustomerBindingSource.DataSource = db.GetCustomers
&lt;SPAN style="COLOR: blue"&gt;End Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;To save the data in a connected state like this we can simply call SubmitChanges on the DataContext. The LINQ to SQL DataContext tracks the state of each modification, deletion or insertion into the collection of Customers and will call the corresponding stored procedures that we configured.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Private Sub &lt;/SPAN&gt;CustomerBindingNavigatorSaveItem_Click() _
   &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;CustomerBindingNavigatorSaveItem.Click
   &lt;SPAN style="COLOR: blue"&gt;Try
       &lt;/SPAN&gt;db.SubmitChanges()

       MsgBox(&lt;SPAN style="COLOR: #a31515"&gt;"saved"&lt;/SPAN&gt;)
   &lt;SPAN style="COLOR: blue"&gt;Catch &lt;/SPAN&gt;ex &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;Exception
       MsgBox(ex.ToString)
   &lt;SPAN style="COLOR: blue"&gt;End Try
End Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;(Note: In order to get get drag-drop data binding in Windows Forms to work with LINQ to SQL objects you need to select Data --&amp;gt; "Add New Data Source" on the main menu, then select Object (not database). Then select the Customer object and Finish. This will populate your Data Sources window so you can drag the grid onto a Windows form.)&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Entity Splitting Using the Entity Data Model&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;This scenario becomes pretty straight-forward and automatic using the &lt;A href="http://msdn.microsoft.com/en-us/library/bb399183.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/bb399183.aspx"&gt;Entity Framework (EF)&lt;/A&gt; and you're not required to write any stored procedures to get it to work. This is because EF provides more complex mappings out of the box than LINQ to SQL or DataSets. And EF separates your database schema from your object model by providing a mapping layer. If you have &lt;A href="http://msdn.microsoft.com/en-us/vstudio/products/cc533448.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/vstudio/products/cc533448.aspx"&gt;Visual Studio 2008 Service Pack 1&lt;/A&gt; you can create what's called an Entity Data Model which provides an ObjectContext, similar in theory to the LINQ to SQL DataContext above, but it provides many &lt;A href="http://msdn.microsoft.com/en-us/library/cc716779.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/cc716779.aspx"&gt;more mapping features&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;When you add a new Entity Data Model to your project you can choose to generate it from the database or you can create an empty model. For this example I'll choose to generate it from the database and I'll select just the Customer and CustomerContactInfo tables and no stored procedures this time. (Note that I can still map stored procs to the update, insert and delete behaviors if I need to though.)&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity8_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity8_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=289 alt=entity8 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity8_thumb.jpg" width=479 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity8_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;This looks just like our database model except you can see the navigation properties displayed that let us navigate from one entity to related ones. Notice the association is also shown as one-to-(none or)one. To set up the mapping so that it will automatically split the entity for us first we need to adjust the Customer by adding the EmailAddress and Phone. You can select the EmailAddress and Phone properties of the CustomerContactInfo and cut then paste them into the Customer. Then you can delete the CustomerContactInfo class from the design surface. &lt;/P&gt;
&lt;P&gt;Now select the Customer and look at the Mapping Details window. Below the column mappings you will see &amp;lt;Add a Table or View&amp;gt;. Drop that down and select CustomerContactInfo and it will automatically map the columns in that table to the properties that we added.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity9_2.jpg" mce_href="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity9_2.jpg"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=359 alt=entity9 src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity9_thumb.jpg" width=640 border=0 mce_src="http://blogs.msdn.com/blogfiles/bethmassi/WindowsLiveWriter/EditingDatafromTwoTablesinaSingleDataGri_D9C4/entity9_thumb.jpg"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;Save the model and rebuild the project. Now you can add Customer as a data source for drag-drop Winforms data binding the same way you do for LINQ to SQL classes or your own objects. And the code for loading and saving of the Customers is similar to the LINQ to SQL code above except loading the Customers is much more intuitive. And the ObjectContext tracks changes for you and generates the proper insert, update, and delete statements automatically without you having to define stored procedures to do the splitting. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;Private &lt;/SPAN&gt;db &lt;SPAN style="COLOR: blue"&gt;As New &lt;/SPAN&gt;MyDatabaseEntities

&lt;SPAN style="COLOR: blue"&gt;Private Sub &lt;/SPAN&gt;Form1_Load() &lt;SPAN style="COLOR: blue"&gt;Handles MyBase&lt;/SPAN&gt;.Load

    &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.CustomerBindingSource.DataSource = db.Customer
&lt;SPAN style="COLOR: blue"&gt;End Sub

Private Sub &lt;/SPAN&gt;CustomerBindingNavigatorSaveItem_Click() _
    &lt;SPAN style="COLOR: blue"&gt;Handles &lt;/SPAN&gt;CustomerBindingNavigatorSaveItem.Click

    &lt;SPAN style="COLOR: blue"&gt;Try
        &lt;/SPAN&gt;db.SaveChanges()

        MsgBox(&lt;SPAN style="COLOR: #a31515"&gt;"saved"&lt;/SPAN&gt;)
    &lt;SPAN style="COLOR: blue"&gt;Catch &lt;/SPAN&gt;ex &lt;SPAN style="COLOR: blue"&gt;As &lt;/SPAN&gt;Exception
        MsgBox(ex.ToString)
    &lt;SPAN style="COLOR: blue"&gt;End Try
End Sub&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;This is just the tip of the iceberg using the Entity Data Model. I'd recommend reading the &lt;A href="http://msdn.microsoft.com/en-us/library/bb399572.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/bb399572.aspx"&gt;documentation in the MSDN library&lt;/A&gt;, visiting the &lt;A href="http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=533&amp;amp;SiteID=1" target=_blank mce_href="http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=533&amp;amp;SiteID=1"&gt;forums&lt;/A&gt; and the &lt;A href="http://blogs.msdn.com/dsimmons/pages/entity-framework-faq.aspx" target=_blank mce_href="http://blogs.msdn.com/dsimmons/pages/entity-framework-faq.aspx"&gt;FAQ&lt;/A&gt;, as well as visiting &lt;A href="http://learnentityframework.com/events" target=_blank mce_href="http://learnentityframework.com/events"&gt;Julie Lerman's site&lt;/A&gt; (she's been living EF since the early Betas). I'm just learning EF myself but as you can see it allows you to model more complex data scenarios. &lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9001289" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Entity+Framework/default.aspx">Entity Framework</category></item><item><title>LINQ to SQL N-Tier Smart Client - Part 3 Database Transactions</title><link>http://blogs.msdn.com/bethmassi/archive/2008/04/16/linq-to-sql-n-tier-smart-client-part-3-database-transactions.aspx</link><pubDate>Wed, 16 Apr 2008 22:03:41 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8399378</guid><dc:creator>Beth Massi</dc:creator><slash:comments>20</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/8399378.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=8399378</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=8399378</wfw:comment><description>&lt;p&gt;In my previous posts this week I showed how to build a simple distributed application with a Windows client, a WCF hosted middle-tier and a data access layer that used LINQ to SQL:&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/04/12/linq-to-sql-n-tier-smart-client.aspx"&gt;LINQ to SQL N-Tier Smart Client - Part 1 Building the Middle-Tier&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/04/14/linq-to-sql-n-tier-smart-client-part-2-building-the-client.aspx"&gt;LINQ to SQL N-Tier Smart Client - Part 2 Building the Client&lt;/a&gt;&lt;/p&gt; &lt;p&gt;After sleeping on the design I realized that there's a scenario that we may want to handle. When we built the &lt;a href="http://code.msdn.microsoft.com/formsoverlinq" target="_blank"&gt;connected client-server version of the application&lt;/a&gt; (using the connected DataContext), because the DataContext is tracking all our changes (updates/inserts/and deletes) when we call SubmitChanges these updates are all processed in one single database transaction. &lt;/p&gt; &lt;p&gt;This may or may not be required for your application and in the case of Orders/OrderDetails it's okay to allow the updates and inserts and then the deletes to be processed in separate transactions. However what if we were working with drug interactions in a medical application or other data that needs to provide this level of integrity?&lt;/p&gt; &lt;p&gt;It's easy to make these modifications to our n-tier application we built. All we need to do is attach ALL the changes that we want processed in a single database transaction to one instance of the DataContext. To do this first we need to modify our service to accept all our changes. This can end up putting more data on the wire which we discussed in &lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/04/12/linq-to-sql-n-tier-smart-client.aspx" target="_blank"&gt;Part 1&lt;/a&gt; so you need to evaluate your scenarios carefully. In our case I'm only pulling up open orders for a particular customer ID so the data set is relatively small.&lt;/p&gt; &lt;p&gt;First add the following interface on our WCF service:&lt;/p&gt;&lt;pre class="code"&gt;&amp;lt;ServiceContract()&amp;gt; _
&lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Interface&lt;/span&gt; IOMSService&lt;br&gt;.&lt;br&gt;.&lt;br&gt;&amp;lt;OperationContract()&amp;gt; _
&lt;span style="color: rgb(0,0,255)"&gt;Function&lt;/span&gt; SaveAllOrders(&lt;span style="color: rgb(0,0,255)"&gt;ByRef&lt;/span&gt; orders &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; OrderList, _
                       &lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; deletedOrders &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; IEnumerable(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; Order), _
                       &lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; deletedDetails &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; IEnumerable(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; OrderDetail)) &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Boolean
&lt;/span&gt;&lt;/pre&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Interface&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;p&gt;Next add the implementation to the OMSDataManager class in the data access layer to go ahead and attach all the changes to a single DataContext and submit all the changes at once. Note that the validation is performed exactly as before (when SubmitChanges is called).&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Shared&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Function&lt;/span&gt; SaveAllOrders(&lt;span style="color: rgb(0,0,255)"&gt;ByRef&lt;/span&gt; orders &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; IEnumerable(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; Order), _
                                     &lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; deletedOrders &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; IEnumerable(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; Order), _
                                     &lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; deletedDetails &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; IEnumerable(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; OrderDetail)) &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Boolean

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; hasOrders = (orders &lt;span style="color: rgb(0,0,255)"&gt;IsNot&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Nothing&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; orders.Count &amp;gt; 0)
    &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; hasDeletedOrders = (deletedOrders &lt;span style="color: rgb(0,0,255)"&gt;IsNot&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Nothing&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; deletedOrders.Count &amp;gt; 0)
    &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; hasDeletedDetails = (deletedDetails &lt;span style="color: rgb(0,0,255)"&gt;IsNot&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Nothing&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; deletedDetails.Count &amp;gt; 0)

    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; (&lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; hasOrders) &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; (&lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; hasDeletedOrders) &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; (&lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; hasDeletedDetails) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt; &lt;span style="color: rgb(0,128,0)"&gt;'nothing at all to save
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; db &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;New&lt;/span&gt; OMSDataContext

    &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; o &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; orders
        &lt;span style="color: rgb(0,128,0)"&gt;'Insert/update orders and details
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; o.OrderID = 0 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            db.Orders.InsertOnSubmit(o)
        &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;            db.Orders.Attach(o, o.IsDirty)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; d &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; o.OrderDetails
            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; d.IsDirty &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; d.OrderDetailID = 0 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                    db.OrderDetails.InsertOnSubmit(d)
                &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;                    db.OrderDetails.Attach(d, &lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;)
                &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Next

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; hasDeletedOrders &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'Delete orders and related details
&lt;/span&gt;        db.Orders.AttachAll(deletedOrders, &lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;)
        db.Orders.DeleteAllOnSubmit(deletedOrders)&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;        &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; o &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; deletedOrders
             &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; detail &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; o.OrderDetails
                db.OrderDetails.DeleteOnSubmit(detail)
            &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; hasDeletedDetails &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'Now delete the order details that were passed in
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;' (these order parents were not deleted, just the details)
&lt;/span&gt;        db.OrderDetails.AttachAll(deletedDetails, &lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;)
        db.OrderDetails.DeleteAllOnSubmit(deletedDetails)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Try
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'There's one database transaction for all records that are attached.
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'Since we attached all updates/inserts/deletes 
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;' they will all be processed in one transaction.
&lt;/span&gt;        db.SubmitChanges(ConflictMode.ContinueOnConflict)

        &lt;span style="color: rgb(0,128,0)"&gt;'Reset the IsDirty flag
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; o &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; orders
            o.IsDirty = &lt;span style="color: rgb(0,0,255)"&gt;False
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; d &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; o.OrderDetails
                d.IsDirty = &lt;span style="color: rgb(0,0,255)"&gt;False
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Catch&lt;/span&gt; ex &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; ChangeConflictException
        &lt;span style="color: rgb(0,128,0)"&gt;'TODO: Conflict Handling
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Throw
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;False
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Try

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;True
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Function&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;We can then modify our form to call this new operation. On the client form I just added a new method called SaveAll. Note that the same simple change tracking is being used.&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; SaveAll()
    &lt;span style="color: rgb(0,128,0)"&gt;'Push any pending edits on the BindingSources to the BindingList
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.Validate()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.EndEdit()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDetailsBindingSource.EndEdit()
    &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; saved = &lt;span style="color: rgb(0,0,255)"&gt;False

&lt;/span&gt;    &lt;span style="color: rgb(0,128,0)"&gt;'Only save changes if there are some and they are valid
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.HasChanges &lt;span style="color: rgb(0,0,255)"&gt;AndAlso&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.ValidateOrders() &lt;span style="color: rgb(0,0,255)"&gt;Then

&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; saveOrders = &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.Orders.ToArray()
        &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; delOrders = &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DeletedOrders.ToArray()
        &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; delDetails = &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DeletedDetails.ToArray()

        &lt;span style="color: rgb(0,0,255)"&gt;Try
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; saveOrders.Count &amp;gt; 0 &lt;span style="color: rgb(0,0,255)"&gt;OrElse&lt;/span&gt; delOrders.Count &amp;gt; 0 &lt;span style="color: rgb(0,0,255)"&gt;OrElse&lt;/span&gt; delDetails.Count &amp;gt; 0 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,128,0)"&gt;'Update/insert orders/details
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; proxy.SaveAllOrders(saveOrders, delOrders, delDetails) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DeletedDetails.Clear()
                    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DeletedOrders.Clear()
                    saved = &lt;span style="color: rgb(0,0,255)"&gt;True
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Catch&lt;/span&gt; ex &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; Exception
            MsgBox(ex.ToString)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Try

&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'Merges added keys and any validation errors
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.MergeOrdersList(saveOrders)

    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;        &lt;span style="color: rgb(0,128,0)"&gt;'Display any errors if there are any
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DisplayErrors()
        MsgBox(&lt;span style="color: rgb(163,21,21)"&gt;"Please correct the errors on this form."&lt;/span&gt;)
    &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; saved &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            MsgBox(&lt;span style="color: rgb(163,21,21)"&gt;"Your data was saved."&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;            MsgBox(&lt;span style="color: rgb(163,21,21)"&gt;"Your data was not saved."&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;p&gt;So now when we make updates, inserts and deletes to our Orders and OrderDetails then we can save them all in a single database transaction.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blogs.msdn.com/photos/bethmassi/images/8399366/640x392.aspx"&gt; &lt;/p&gt;
&lt;p&gt;I've uploaded the &lt;a href="http://code.msdn.microsoft.com/linqntier" target="_blank"&gt;latest version of the application onto Code Gallery&lt;/a&gt; with the modifications.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8399378" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/WCF/default.aspx">WCF</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/N-tier/default.aspx">N-tier</category></item><item><title>LINQ to SQL N-Tier Smart Client - Part 2 Building the Client</title><link>http://blogs.msdn.com/bethmassi/archive/2008/04/14/linq-to-sql-n-tier-smart-client-part-2-building-the-client.aspx</link><pubDate>Mon, 14 Apr 2008 21:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8386068</guid><dc:creator>Beth Massi</dc:creator><slash:comments>14</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/8386068.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=8386068</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=8386068</wfw:comment><description>&lt;P&gt;In my &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/04/12/linq-to-sql-n-tier-smart-client.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/04/12/linq-to-sql-n-tier-smart-client.aspx"&gt;last post&lt;/A&gt; we built the service and data access layer for our LINQ to SQL N-Tier application. In this post we'll walk through building a very simple Windows client form that works with our middle-tier. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Adding the Service Reference&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now that we have our middle-tier built it's time to add the service reference to the client project. Sine we have both .NET on the server and the client I'm going to use type sharing so that we can reuse the business objects (LINQ to SQL classes) on both ends. If you recall we we already added a project reference on the client to the OMSDataLayer project that defines these types.&lt;/P&gt;
&lt;P&gt;Once you add that project reference we can add the service reference by right-clicking on the client and selecting "Add Service Reference" which opens up the Visual Studio 2008 Add Service Reference dialog. Hit the Discover button and it will pick up the OMSService in our solution. Click on the "Advanced" button and you'll notice some interesting settings here that I should mention.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/8385809/538x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/8385809/538x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Note here that the default is to "Reuse types in all referenced assemblies". This means that since we added the project reference to our LINQ to SQL business objects first, when the service proxy is generated it will &lt;STRONG&gt;not&lt;/STRONG&gt; create new classes on the client, instead it will reference our business object types directly. Although this can make versioning more of a challenge it drastically cuts down the amount of code we have to write to maintain our business rules because now they are shared. However note that rules we call from the client cannot access the database directly. Our application here does not have any rules like that but it's something you may need to code for in your scenarios.&lt;/P&gt;
&lt;P&gt;The other interesting settings I'll mention are the Collection type and Dictionary collection type settings since we're passing these types from our service. You can set these types to serialize differently if you need to. For instance, you can set the collection type to a BindingList if you are going to use all the collections from this service in typical data binding scenarios. Since this setting is for the entire service and we're only going to need a BindingList for just our GetOrdersByCustomerID result, I'm opting to keep the default Array type instead.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Loading the Data&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now we're ready to build our n-tier master-detail (one-to-many) form. Create a new form and then add a new data source (Menu, Data --&amp;gt; Add New Data Source) and select Object. Then expand the OMSDataLayer and choose the Order object and then do it again for Product. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/8385865/486x375.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/8385865/486x375.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Now we can build the master-detail form &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx"&gt;like I showed in this post&lt;/A&gt; (see the "&lt;EM&gt;Data Sources and Data Binding the Form" &lt;/EM&gt;section) but this time against the objects in the shared assembly. The other main difference is that we don't need the Customer object because we're going to limit our data to just one customer.&lt;/P&gt;
&lt;P&gt;Now we're ready to create an instance of our service reference and load the Orders from the middle-tier. Since the list will deserialize as an array, I'm going to place them into a BindingList that the form will manage. This will give us automatic add/delete support to the collection and a better data binding experience. I'm also going to set up a couple lists to track deletes of Order and OrderDetails. In a real application typically you create your own subclass of the BindingList and have it track these things but I'm trying to keep this example simple. We'll also load the products just like we did before but this time in our query we call the service instead. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; NtierMasterDetailForm

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; customerID &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Integer&lt;/SPAN&gt; = 1 &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'should come from a search form

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; proxy &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSServiceReference.OMSServiceClient

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; Orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; BindingList(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; DeletedOrders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; List(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; DeletedDetails &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; List(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OrderDetail)

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; Form1_Load() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;MyBase&lt;/SPAN&gt;.Load

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Load the orders from our service
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; orderList = proxy.GetOrdersByCustomerID(customerID)

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; orderList
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders.Add(o)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next

&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.DataSource = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; emptyProduct &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Product() = _
                {&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; Product &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;With&lt;/SPAN&gt; {.Name = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"&amp;lt;Select a product&amp;gt;"&lt;/SPAN&gt;, .ProductID = 0}}

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.ProductBindingSource.DataSource = (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; Empty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; emptyProduct).Union( _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; Product &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; proxy.GetProductList _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;By&lt;/SPAN&gt; Product.Name)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;Tracking Changes on the Objects&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now let's see how we're going to track all the changes made to the Orders and OrderDetails. First let's take another look at our BaseBusiness class. This is the class that we &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx"&gt;created in this post&lt;/A&gt; when we implemented our validation. When we built the middle-tier I mentioned that we needed to add this property but it's the client that needs to set it. Here's a look at the modifications we need to make to the BaseBusiness object including adding the DataMember attribute to the new IsDirty property as well as on the ValidationErrors dictionary.&lt;/P&gt;&lt;PRE class=code&gt;&amp;lt;DataContract()&amp;gt; _
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; BaseBusiness
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IDataErrorInfo

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; m_isDirty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;    &amp;lt;DataMember()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Property&lt;/SPAN&gt; IsDirty() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Get
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; m_isDirty
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Get
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Set&lt;/SPAN&gt;(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; value &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean&lt;/SPAN&gt;)
            m_isDirty = value
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Set
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Property

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'This dictionary contains a list of our validation errors for each field
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; m_validationErrors &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; Dictionary(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;)

    &amp;lt;DataMember()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Property&lt;/SPAN&gt; ValidationErrors() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Dictionary(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Get
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; m_validationErrors
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Get
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Set&lt;/SPAN&gt;(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; value &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Dictionary(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;String&lt;/SPAN&gt;))
            m_validationErrors = value
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Set
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Property&lt;BR&gt;.&lt;BR&gt;.&lt;BR&gt;.&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Since LINQ to SQL classes implement IPropertyNotifyChanged we can handle this event to set the IsDirty flag. The easiest way to set this flag is to tell the business objects themselves to do it. In order to hook up this event handler again when the objects are deserialized from the WCF service we can attribute a method with the &lt;A href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx"&gt;OnDeserializedAttribute&lt;/A&gt; and add an event handler to the PropertyChanged event on all our business objects. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Partial&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; Order
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Inherits&lt;/SPAN&gt; BaseBusiness

    &amp;lt;OnDeserialized()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; OnDeserialized(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; context &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; StreamingContext)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AddHandler&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.PropertyChanged, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AddressOf&lt;/SPAN&gt; MyPropertyChanged
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; MyPropertyChanged(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Object&lt;/SPAN&gt;, &lt;BR&gt;                                  &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; e &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; System.ComponentModel.PropertyChangedEventArgs) _&lt;BR&gt;                                  &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.PropertyChanged
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; e.PropertyName &amp;lt;&amp;gt; &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Customer"&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.IsDirty = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;BR&gt;.&lt;BR&gt;.&lt;BR&gt;.&lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;
&lt;P&gt;The trick in the handler is to set the IsDirty flag only if the entity reference (the parent reference) property is not being set because we want to only set this flag if the user is making changes, not when the collection reference is set by the system.&lt;/P&gt;
&lt;P&gt;Tracking adds is really easy because when an object is added to the collection it will be sent to the middle-tier and we can use the primary keys to determine if the Order or OrderDetail is new. For instance, if the OrderID on the Order is equal to zero (OrderID = 0) then we know we have a new object in the collection. &lt;/P&gt;
&lt;P&gt;Deletes are a bit trickier because when you delete an object from the collection it's gone. If you are implementing a custom BindingList then you can just override the RemoveItem method but in our simple form we're just going to add the Order or OrderDetail being deleted to our Deleted* lists when the delete buttons are clicked on the form.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; OrderNavigatorDeleteItem_Click() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; BindingNavigatorDeleteItem.Click
    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Track deletes of orders
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Position &amp;gt; -1 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; order &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Order = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Current, Order)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; order.OrderID &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Greater than 0 indicates that the object came from the database.
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'If it's = 0 then we know the object was added here then deleted 
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'  and we don't need to track that.
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedOrders.Add(order)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; DetailNavigatorDeleteItem_Click() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; DetailNavigatorDeleteItem.Click
    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Track deletes of details
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderDetailsBindingSource.Position &amp;gt; -1 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; detail &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; OrderDetail = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderDetailsBindingSource.Current, OrderDetail)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; detail.OrderDetailID &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedDetails.Add(detail)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub
&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;&lt;STRONG&gt;Validating and Saving our Changes&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Before we send the changes to the service on the middle-tier we should validate the business objects here to save a round-trip. When we were working with the LINQ to SQL DataContext in connected mode the objects were validated when we called SubmitChanges(). This still happens in our middle-tier code but we need to validate here on the client as well so I added a public Validate method to the LINQ to SQL partial classes that just simply call into the OnValidate private methods we &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx"&gt;wrote previously&lt;/A&gt;. In the case of Order we'll also validate any OrderDetails.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Partial&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; Order
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Inherits&lt;/SPAN&gt; BaseBusiness&lt;BR&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;    .&lt;BR&gt;    .&lt;BR&gt;    .&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; 
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; Validate()
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OnValidate(System.Data.Linq.ChangeAction.None)

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Validate the OrderDetails if there are any
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; d &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderDetails
            d.Validate()
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;BR&gt;.&lt;BR&gt;.&lt;BR&gt;.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Now we're ready to write our save code. If everything validates here on the client we first then send the deletes to the middle-tier, and if all goes well there then we clear the lists where we were tracking those objects. Then we can send the added and updated rows into the middle-tier. The middle-tier will then perform the validation there and then update and insert the business objects, and return the added primary/foreign keys. If we had any additional middle-tier business rules then those would also run and we could add additional validation messages that would be sent back in the ValidationErrors collection on each object.&lt;/P&gt;
&lt;P&gt;The last thing left to do is dump the collection coming back from the middle-tier with our added keys back into the BindingList on our form. We just need to suspend the data binding first then we can copy the array back into the BindingList collection. Here's all the save code and supporting form methods.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; OrderBindingNavigatorSaveItem_Click() _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; OrderBindingNavigatorSaveItem.Click

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Save()
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' Saves all changes to the middle-tier
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; Save()
    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Push any pending edits on the BindingSources to the BindingList
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Validate()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.EndEdit()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderDetailsBindingSource.EndEdit()

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; saved = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Only save changes if there are some and they are valid
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.HasChanges &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AndAlso&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.ValidateOrders() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then

&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; saveOrders = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders.ToArray&lt;BR&gt;
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedDetails.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrElse&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedOrders.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;                &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Delete any orders/details
&lt;/SPAN&gt;                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; proxy.DeleteOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedOrders.ToArray, _
                                      &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedDetails.ToArray) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then

&lt;/SPAN&gt;                    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedDetails.Clear()
                    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedOrders.Clear()
                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Else
&lt;/SPAN&gt;                    saved = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; saved &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; saveOrders.Length &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;                    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Update/insert orders/details
&lt;/SPAN&gt;                    saved = proxy.SaveOrders(saveOrders)
                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Exception
            MsgBox(ex.ToString)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try

&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'merges added keys and any validation errors from the middle-tier
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.MergeOrdersList(saveOrders)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.HasErrors &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Display any errors if there are any (same technique as &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx"&gt;before&lt;/A&gt;)
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DisplayErrors()
        MsgBox(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Please correct the errors on this form."&lt;/SPAN&gt;)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Else
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; saved &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            MsgBox(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Your data was saved."&lt;/SPAN&gt;)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Else
&lt;/SPAN&gt;            MsgBox(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Your data was not saved."&lt;/SPAN&gt;)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' Returns True if there are any validation errors on the business objects.
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; HasErrors() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; o.HasErrors &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' Validates all the orders (order details are validated in the Order.Validate)
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; ValidateOrders() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders
            o.Validate()
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; ValidationException
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' Returns True if there are any changes to any of the orders/details.
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; HasChanges() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedDetails.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrElse&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.DeletedOrders.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; o.IsDirty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; d &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; o.OrderDetails
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; d.IsDirty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' Copies from array to the BindingList while suspending data binding
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;param name="changes"&amp;gt;&amp;lt;/param&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;''' &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; MergeOrdersList(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; changes() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Order)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; pos = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Position
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.SuspendBinding()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.RaiseListChangedEvents = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False&lt;BR&gt;
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders.Clear()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; changes
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Orders.Add(o)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.ResumeBinding()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.RaiseListChangedEvents = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Position = pos
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;And that's basically it. As you can see even in it's simplest implementation (that I could think of) writing n-tier applications with LINQ to SQL takes some work, especially as the relations between our object collections increase. LINQ to SQL is really just used as the data access technology in the middle-tier, everything on top of that is up to us to implement as we see fit for our particular scenarios. &lt;/P&gt;
&lt;P&gt;You can &lt;A class="" href="http://code.msdn.microsoft.com/linqntier/" target=_blank mce_href="http://code.msdn.microsoft.com/linqntier/"&gt;download the sample application on CodeGallery here&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8386068" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/WCF/default.aspx">WCF</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/N-tier/default.aspx">N-tier</category></item><item><title>LINQ to SQL N-Tier Smart Client - Part 1 Building the Middle-Tier</title><link>http://blogs.msdn.com/bethmassi/archive/2008/04/12/linq-to-sql-n-tier-smart-client.aspx</link><pubDate>Sat, 12 Apr 2008 19:01:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8376294</guid><dc:creator>Beth Massi</dc:creator><slash:comments>42</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/8376294.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=8376294</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=8376294</wfw:comment><description>&lt;P&gt;In my previous posts on LINQ to SQL I showed how to build LINQ to SQL classes and set up the data binding in your Windows applications. If you missed them:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx" mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx"&gt;Related Data Binding and ComboBoxes with LINQ to SQL&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx" mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx"&gt;Creating Lookup Lists with LINQ to SQL&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx" mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx"&gt;One-To-Many (Master-Detail) Forms with LINQ to SQL&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx" mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx"&gt;Simple Validation with LINQ to SQL Classes&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;These articles focus on the binding and validation and use a connected model; meaning that the DataContext is alive and available tracking the changes we make to the collections of LINQ to SQL objects. Out of the box, LINQ to SQL is really easy to get working with a connected client-server architecture. As long as your objects are attached to the DataContext you get all the nice features like change tracking and automatic lazy loading of related collections. However, unlike the DataSet, once you disconnect from the data source you lose all automatic change tracking. So why use LINQ to SQL objects and not &lt;A href="http://msdn2.microsoft.com/en-us/vbasic/cc138242.aspx?wt.slv=topsectionsee" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/cc138242.aspx?wt.slv=topsectionsee"&gt;distributed DataSets&lt;/A&gt;?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;The Great Debate - DataSets or objects?&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;It depends on your particular scenarios which approach you would take, and you are not limited to using just one approach in your application depending on your scenario. I always try to keep these guidelines in mind when I'm trying to decide which approach to take:&lt;/P&gt;
&lt;P&gt;- DataSets are easy to define, serialize, and can now be easily separated from the data access logic in the TableAdapters. (Take a look at how to do this in VS 2008 &lt;A href="http://msdn2.microsoft.com/en-us/vbasic/cc138242.aspx?wt.slv=topsectionsee" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/cc138242.aspx?wt.slv=topsectionsee"&gt;in this video&lt;/A&gt;)&lt;/P&gt;
&lt;P&gt;-DataSets have built-in change tracking, batched editing, viewing and filtering (also supports LINQ), and handles complex relationships automatically&lt;/P&gt;
&lt;P&gt;-DataSets are easy to use with advanced DataBinding scenarios&lt;/P&gt;
&lt;P&gt;-DataSets are easy to implement simple field and row validation but become challenging when complex behavior needs to be coded, they cannot inherit from a business class so complex business logic and data become separated&lt;/P&gt;
&lt;P&gt;-DataSets should not be used if your clients are not all .NET clients because this type is a very complex type specific to .NET&lt;/P&gt;
&lt;P&gt;-LINQ to SQL classes are plain old CLR objects (POCO) that only implement a couple property changed notification interfaces&lt;/P&gt;
&lt;P&gt;-LINQ to SQL classes allow you full control over the validation, behavior, business logic, and can inherit from any class you provide so you can create a base business class&lt;/P&gt;
&lt;P&gt;-LINQ to SQL object collections can be serialized as arrays to non-.NET clients&lt;/P&gt;
&lt;P&gt;-LINQ to SQL objects, once detached from the DataContext, do not track changes or support any advanced DataBinding features out of the box, those must be implemented by you and the code is often non-trivial&lt;/P&gt;
&lt;P&gt;Personally I like both approaches, it just depends on the scenario. Typically if I have a batch .NET data-entry form that I need to service with only a few field or column validations necessary, DataSets are my first choice, especially if there are more than a couple related tables. They are also a good choice for reports and simple view-only forms. If I have more complex rules and behavior or I need full control over my object hierarchy then POCO business objects are the better choice. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Going N-Tier with LINQ to SQL &lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you're ready to jump off the cliff there's some &lt;A href="http://msdn2.microsoft.com/en-us/library/bb882661.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb882661.aspx"&gt;really good information in the MSDN Library&lt;/A&gt; on how to set up LINQ to SQL in distributed applications. There are a variety of ways to handle LINQ to SQL in these scenarios as the library shows. In this post I'm going to walk through the most common scenarios to get this to work with a remote .NET Windows smart client (WPF coming soon!).&amp;nbsp; This post will focus on the data access and service layer and I'll follow up with the client in the next post. &lt;/P&gt;
&lt;P&gt;We're going to continue with the application that we built in the previous articles which was just a single Windows forms client built into one EXE. What we need to do is modify the design by adding a data access layer that communicates with our database as well as a service layer that the client will communicate with. Since our client and our server are both written in .NET I am also going to reuse the LINQ to SQL objects that contain our validation so we can keep those rules in one place. Please note that this is &lt;STRONG&gt;not&lt;/STRONG&gt; SOA (Service Oriented Architecture). If you need to go that route then I highly recommend &lt;A href="http://www.amazon.com/Programming-WCF-Services-Juval-Lowy/dp/0596526997" target=_blank mce_href="http://www.amazon.com/Programming-WCF-Services-Juval-Lowy/dp/0596526997"&gt;this book&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;So here's a diagram of what we're going to implement.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/8376321/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/8376321/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;The first thing to do is add a Class Library project called OMSDataLayer to contain the data access layer and the generated LINQ to SQL classes (our business objects) along with the partial class code and base business object class. Unfortunately the O/R designer doesn't let you separate the generated classes from the DataContext object (database access) like the DataSet designer does so if you really want to be a stickler and you have a large project you're developing you should create your own classes and then you can decorate them with the right attributes to get them to work with LINQ to SQL or use an &lt;A href="http://msdn2.microsoft.com/en-us/library/bb386907.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb386907.aspx"&gt;external mapping file&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;Next add a reference to System.Data.Linq to the class library and then move all the associated .dbml files as well as the BaseBusiness and ValidationException classes into the project. Then add a project reference to this OMSDataLayer to the client project. At this point if I want the client to compile and work exactly like before I can add a project level imports to the OMSDataLayer on the client, Import the OMSDataLayer namespace, and run it like before (we just have to go through and change the namespace on our objects on our form). &lt;/P&gt;
&lt;P&gt;Finally, add a new WCF Service project to the solution and add the OMSDataLayer as a project reference. Now we've got the structure of our solution set up.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/8376646/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/8376646/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Contract First&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now it's time to think about the interaction our client will need to have with our service. It's always a good idea to think about how the endpoints in your distributed applications need to communicate with each other before you start developing them. For this application we only need interfaces for retrieval of the Orders and Products and saving and deleting Orders and OrderDetails. So I defined the following operations:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Imports&lt;/SPAN&gt; OMSDataLayer

&amp;lt;ServiceContract()&amp;gt; _
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Interface&lt;/SPAN&gt; IOMSService

    &amp;lt;OperationContract()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetOrdersByCustomerID(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; customerID &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Integer&lt;/SPAN&gt;) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)

    &amp;lt;OperationContract()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetProductList() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Product)

    &amp;lt;OperationContract()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; SaveOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByRef&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; OrderList) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean

&lt;/SPAN&gt;    &amp;lt;OperationContract()&amp;gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; DeleteOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order), _
                          &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; details &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OrderDetail)) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean

End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Interface&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;I'm just returning a collection of our business objects in the Get* methods and I've defined Save and Delete methods that accept a collection of orders to either Save (update or insert) and Delete against the database. I'm sending Orders and OrderDetail collections in the case of Delete because I implemented a very simple change-tracking strategy and I want to be able to delete OrderDetails independently. OrderList is a simple Serializable class definition that inherits from List(Of Order) and has the KnownType attribute applied. This is needed so I can pass the Orders collection by reference so that primary keys and validation messages are sent back on the objects cleanly. (There are many ways to do this like passing messages and keys back separately. This is the easiest and cleanest in my opinion but you need to be aware that there is more data on the wire in this approach.)&lt;/P&gt;&lt;PRE class=code&gt;&amp;lt;KnownType(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;GetType&lt;/SPAN&gt;(Order))&amp;gt; _
&amp;lt;Serializable()&amp;gt; _
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; OrderList
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Inherits&lt;/SPAN&gt; List(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)

&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;The last step to defining our contract is to set up our business objects so that they can be serialized as a &lt;A href="http://msdn2.microsoft.com/en-us/library/ms733127.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms733127.aspx"&gt;WCF DataContract&lt;/A&gt;. We can do this via the O/R designer. Just open up the OMS.dbml file and in the properties for the DataContext set the Serialization mode to "Unidirectional".&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/8376916/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/8376916/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;This adds the &lt;A href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx"&gt;DataContract&lt;/A&gt; attribute to the classes and the &lt;A href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.aspx"&gt;DataMember&lt;/A&gt; attribute to the properties. If we're using a WCF service with the &lt;A href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx"&gt;DataContractSerializer&lt;/A&gt; then all this will work just dandy. This is because this serializer supports bi-directional relationships (like we have with Order and OrderDetail). However if we were trying to serialize this with the XMLSerializer used with non-WCF services we'd have issues; you'd only be allowed to serialize objects with no cyclic relationships. &lt;/P&gt;
&lt;P&gt;We also need to add the DataContract and DataMember attributes to our BaseBusiness class as well because our objects inherit from this class. Also add a property called IsDirty to the BaseBusiness class that will help us later when we need to implement change tracking on our client. In the next post I'll talk about my approach to doing that when we talk about the client.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Disconnected Retrieval&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now that we have our contracts and solution set up we're ready to start partying on the data and write our data access layer code. Let's start with retrieving our Products and Orders. To recap what I mentioned in previous articles, when working in connected mode with the DataContext, collections of objects are not retrieved until they are &lt;EM&gt;accessed&lt;/EM&gt;. This means that if I write a LINQ query to select an Order from the database it will not execute any statements to retrieve the OrderDetails until I attempt to access that child collection. This is a good thing because it saves calls to the database if they are not needed. &lt;/P&gt;
&lt;P&gt;However when we are disconnected like in a distributed application there is no automatic lazy loading available, you have to roll your own. You need to decide how chatty you want to be in favor of less data on the wire. If you expect the client to be scrolling through smaller child collections it's probably better to just bring down a larger chunk of data in one call instead of making the client call the service multiple times. (Just to be clear, the client can still perform LINQ queries on the collections on the client-side to do additional filtering but that will be performed with standard LINQ to Objects, LINQ to SQL is only used on our server in the middle-tier.)&lt;/P&gt;
&lt;P&gt;In this example, we won't need any child collections filled on Products, we're just using it as a lookup list. However, with Orders I want to pull all the Orders that haven't shipped for a specific customer ID and I want to also pull down those OrderDetails all in one call. &lt;/P&gt;
&lt;P&gt;So now create a OMSDataManager class in our data access layer which our service implementation can call into. It's really easy to retrieve the Products. Note that the objects are automatically detached from the DataContext when they are serialized to the client through the service so we don't have to explicitly detach them here.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Imports&lt;/SPAN&gt; System.Data.Linq&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; OMSDataManager

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Shared&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetProductsList() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Product)
        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Create the Datacontext
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSDataContext

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Return the list of products. &lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;        'Objects are detached from the DataContext &lt;BR&gt;       &lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' automatically on serialization through the service&lt;BR&gt;&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; db.Products.AsEnumerable()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Now with Orders because we want load the OrderDetails as well we need to specify this in a &lt;A href="http://msdn2.microsoft.com/en-us/library/system.data.linq.dataloadoptions.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.data.linq.dataloadoptions.aspx"&gt;DataLoadOptions&lt;/A&gt; object and pass them to the DataContext. The way we use this object is we call the LoadWith method to specify which data related to the main target should also be retrieved at the same time, resulting in one trip to the database. You pass it a Lambda expression to specify this. So to retrieve the Orders &lt;STRONG&gt;and &lt;/STRONG&gt;OrderDetails in one LINQ to SQL query we can write:&lt;/P&gt;&lt;PRE class=code&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Shared&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetOrdersByCustomerID(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; customerID &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Integer&lt;/SPAN&gt;) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)
    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Create the Datacontext
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSDataContext

    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'We want to also retrieve the OrderDetails collection as well.
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; dlo &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; DataLoadOptions()
    dlo.LoadWith(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt;(o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Order) o.OrderDetails)
    db.LoadOptions = dlo

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; orders = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; order &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; db.Orders _
                 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Select&lt;/SPAN&gt; order _
                 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Where&lt;/SPAN&gt; order.CustomerID = customerID &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AndAlso&lt;/SPAN&gt; _
                       order.ShipDate &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Is&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Nothing

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; orders.AsEnumerable()
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;&lt;STRONG&gt;Disconnected Updates and Deletes&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Disconnected updates are a bit more involved from a decision-making point of view. The way you update your data depends on the schema of your database and how you are performing concurrency checking. &lt;A href="http://msdn2.microsoft.com/en-us/library/bb546187.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/bb546187.aspx"&gt;The documentation in the library&lt;/A&gt; goes into details on all the ways you can update your data but the simplest is using a timestamp field to track row modifications.&lt;/P&gt;
&lt;P&gt;In our OMS database I have a "Modified" field specified on every table and I'm using that field to perform the concurrency checks. This makes it much easier to deal with updates on our middle-tier because that field's value is used to compare with the property value on the object and the update will only succeed if the values match. If you don't provide a RowVersion mechanism like a timestamp on your tables then you have to pass all the original values into the middle-tier as well. So I'm all for saving space on the wire and writing less code and timestamps are a common way to do row versioning and concurrency checking so I feel that this is the best approach for most applications. &lt;/P&gt;
&lt;P&gt;The other issue you need to consider is what the best way is to work with your data. Is it better to update one entity at a time (for instance just sending one Order at a time to save) or is it better to accept a collection of updates and make just one chunky call to the middle-tier? Or a combination? I've almost always gone with the batch editing approach in the types of Windows applications I've written. Although there is more data on the wire, there's less calls to the middle-tier. So the client form caches all the updates, inserts and deletes to the collections and passes that around. You really need to watch how much data you're passing around, so in this example we're only pulling up open orders for a particular customer. Remember every scenario needs to be evaluated carefully. I took a simple approach, more data on the wire but 2 calls to the middle-tier, one to update/insert data and one call to delete data. &lt;/P&gt;
&lt;P&gt;You can streamline this more by only sending the changes to the middle-tier from the client but then you need more code to figure out how to merge the middle-tier changes (validation messages and keys, for instance) back into the client. This is really easy to do with if we were using DataSets but for this example I took to simple route and am passing the entire collection -- but be warned -- this approach may not scale depending on your situation. As always, your mileage may vary.&lt;/P&gt;
&lt;P&gt;So let's write our Save code for our Orders. Unlike when using DataSets with the TableAdapter, the DataContext in disconnected mode needs to be told explicitly what to do, insert, update or delete. An easy way to tell whether the objects in our collection are added or modified is by checking the primary key; if it's less then 1 then we know we have a new object to save. We also use the IsDirty flag we added on our BaseBusiness class to determine how to attach our objects to the DataContext.&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Shared&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; SaveOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order)) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&lt;FONT color=#000000&gt;    &lt;/FONT&gt;If&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Is&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Nothing&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrElse&lt;/SPAN&gt; orders.Count = 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSDataContext

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; orders
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; o.OrderID = 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            db.Orders.InsertOnSubmit(o)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Else
&lt;/SPAN&gt;            db.Orders.Attach(o, o.IsDirty)
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; d &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; o.OrderDetails
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; d.IsDirty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; d.OrderDetailID = 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;                    db.OrderDetails.InsertOnSubmit(d)
                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Else
&lt;/SPAN&gt;                    db.OrderDetails.Attach(d, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True&lt;/SPAN&gt;)
                &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'This will continue to process the 
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'rest of the orders even if one fails
&lt;/SPAN&gt;        db.SubmitChanges(ConflictMode.ContinueOnConflict)

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Reset the IsDirty flag
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; orders
            o.IsDirty = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; d &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; o.OrderDetails
                d.IsDirty = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; ChangeConflictException
        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'TODO: Conflict Handling
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Throw
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Disconnected deletes of Orders in our case are very easy because of the way we set up how deletes should work by manually setting setting the DeleteOnNull attribute to true (&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx"&gt;see the end of this post for info on that&lt;/A&gt;). We just need to attach the incoming Orders and OrderDetails to the DataContext and then we can delete them all. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Shared&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; DeleteOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; Order), _
                                    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; details &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OrderDetail)) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSDataContext
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; submit = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;IsNot&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Nothing&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AndAlso&lt;/SPAN&gt; orders.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Delete orders and related details
&lt;/SPAN&gt;        db.Orders.AttachAll(orders, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False&lt;/SPAN&gt;)
        db.Orders.DeleteAllOnSubmit(orders)

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; o &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; orders
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; detail &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; o.OrderDetails
                db.OrderDetails.DeleteOnSubmit(detail)
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;        submit = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; details &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;IsNot&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Nothing&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;AndAlso&lt;/SPAN&gt; details.Count &amp;gt; 0 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Now delete the order details that were passed in
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' (these order parents were not deleted, just the details)
&lt;/SPAN&gt;        db.OrderDetails.AttachAll(details, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False&lt;/SPAN&gt;)
        db.OrderDetails.DeleteAllOnSubmit(details)
        submit = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; submit &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then
&lt;/SPAN&gt;            db.SubmitChanges(ConflictMode.ContinueOnConflict)
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; ChangeConflictException
        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'TODO: Conflict Handling
&lt;/SPAN&gt;         &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;The last thing we need to do to our middle-tier is call the data access layer from our service implementation. Note that I'm catching the ValidationException that's thrown from our business objects if they are invalid when the call to db.SubmitChanges is made and just returning false, which keeps our service from entering the fault state. The client will also perform validation of our business objects before the data is submitted to save a round-trip but the validation is also run here on the middle-tier. Validation messages are collected in a dictionary and serialized back to the client. (&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx"&gt;Read this post&lt;/A&gt; on how we set up validation on our LINQ to SQL classes.)&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Imports&lt;/SPAN&gt; OMSDataLayer

&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; OMSService
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IOMSService

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt;()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetOrdersByCustomerID(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; customerID &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Integer&lt;/SPAN&gt;) _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OMSDataLayer.Order) _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IOMSService.GetOrdersByCustomerID

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; OMSDataManager.GetOrdersByCustomerID(customerID)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; GetProductList() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OMSDataLayer.Product) _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IOMSService.GetProductList

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; OMSDataManager.GetProductsList()
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; DeleteOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OMSDataLayer.Order), _
                                 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByVal&lt;/SPAN&gt; details &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; IEnumerable(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Of&lt;/SPAN&gt; OMSDataLayer.OrderDetail)) _
                                 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean&lt;/SPAN&gt; _
                                 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IOMSService.DeleteOrders

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; OMSDataManager.DeleteOrders(orders, details)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function&lt;/SPAN&gt; SaveOrders(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ByRef&lt;/SPAN&gt; orders &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; OrderList) &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Boolean&lt;/SPAN&gt; _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Implements&lt;/SPAN&gt; IOMSService.SaveOrders

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; OMSDataManager.SaveOrders(orders)

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; ValidationException
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Return&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;False
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Function
End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;So that's the meat of our middle-tier. In the next post we'll build our client form and implement a simple technique for change tracking.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;UPDATE: &lt;/STRONG&gt;&lt;A class="" href="http://blogs.msdn.com/bethmassi/archive/2008/04/14/linq-to-sql-n-tier-smart-client-part-2-building-the-client.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/04/14/linq-to-sql-n-tier-smart-client-part-2-building-the-client.aspx"&gt;Read how to create the client in this post.&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8376294" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/WCF/default.aspx">WCF</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/N-tier/default.aspx">N-tier</category></item><item><title>Simple Validation with LINQ to SQL Classes</title><link>http://blogs.msdn.com/bethmassi/archive/2008/02/25/simple-validation-with-linq-to-sql-classes.aspx</link><pubDate>Tue, 26 Feb 2008 04:04:24 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7897743</guid><dc:creator>Beth Massi</dc:creator><slash:comments>49</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/7897743.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=7897743</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=7897743</wfw:comment><description>&lt;p&gt;In the last few posts on LINQ to SQL I've showed how to set up an object model using the O/R designer and how to handle a couple data binding scenarios with Comboboxes &lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx" target="_blank"&gt;here&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx" target="_blank"&gt;here&lt;/a&gt;. &lt;a href="http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx" target="_blank"&gt;Last post&lt;/a&gt; on this topic we implemented a one-to-many data entry form and I showed how to work with stored procs as well as how to properly configure delete behaviors. In this post I want to explore how to easily add validation rules to our LINQ to SQL classes and how we can get these rules automatically displayed in the UI. &lt;/p&gt; &lt;p&gt;&lt;strong&gt;LINQ to SQL Classes -- A Closer Look&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Classes that implement the IDataErrorInfo interface in conjunction with INotifyPropertyChanged are able to automatically notify UI objects like the ErrorProvider and the DataGridView to display validation errors. LINQ to SQL classes that are generated for you when you create your object models already implement the INotifyPropertyChanged interface and using partial methods we can easily add validation to our classes. For this example we'll expand the One-to-Many data entry form we built to include some business rules. &lt;/p&gt; &lt;p&gt;First let's open up the generated LINQ to SQL classes by opening the .Designer.vb file under the dbml file (if you don't see the designer file, just click the "show all files" button on the Solution Explorer tool strip first). If we take a look at our Order class we will see the following class definition:&lt;/p&gt;&lt;pre class="code"&gt;&amp;lt;Table(Name:=&lt;span style="color: rgb(163,21,21)"&gt;"dbo.Orders"&lt;/span&gt;)&amp;gt;  _
&lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; [Order]
    &lt;span style="color: rgb(0,0,255)"&gt;Implements&lt;/span&gt; System.ComponentModel.INotifyPropertyChanging, _&lt;br&gt;               System.ComponentModel.INotifyPropertyChanged&lt;/pre&gt;
&lt;p&gt;As you can see, this class is just a plain old CLR object (POCO) that implements interfaces that notify when properties are changing or changed on the class. That's it. It's the DataContext that does the heavy lifting, knowing what objects have changes, which ones were added and removed and how to persist these to the database. It does its simple mapping via the attributes on the class and it's properties. Here's the property for CustomerID on our Order class:&lt;/p&gt;&lt;pre class="code"&gt;&amp;lt;Column(Storage:=&lt;span style="color: rgb(163,21,21)"&gt;"_CustomerID"&lt;/span&gt;, DbType:=&lt;span style="color: rgb(163,21,21)"&gt;"Int NOT NULL"&lt;/span&gt;, UpdateCheck:=UpdateCheck.Never)&amp;gt;  _
    &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt; CustomerID() &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Integer
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;._CustomerID
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Set
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; ((&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;._CustomerID = value)  _
                        = &lt;span style="color: rgb(0,0,255)"&gt;false&lt;/span&gt;) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;._Customer.HasLoadedOrAssignedValue &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;Throw&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;New&lt;/span&gt; System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException
                &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OnCustomerIDChanging(value)
                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.SendPropertyChanging
                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;._CustomerID = value
                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.SendPropertyChanged(&lt;span style="color: rgb(163,21,21)"&gt;"CustomerID"&lt;/span&gt;)
                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OnCustomerIDChanged
            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Set
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Partial Classes and Methods&lt;/strong&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In order to add validation and business rules we can add code to the OnCustomerIDChanging and OnCustomerIDChanged methods. But we don't add them here in this generated file, instead we add them into the Partial Class. This is possible because of a new feature in Visual Studio 2008 called Partial Methods. Partial Classes were introduced in Visual Studio 2005 as a way to extend generated classes with additional functionality by allowing you to split classes across physical files. You can add new methods or properties in order to extend a generated class easily by using the Partial Class keyword and then writing your own code. The compiler will "merge" these files into one class. &lt;/p&gt;
&lt;p&gt;In Visual Studio 2008 we can go a step further using Partial methods. Instead of raising and handling private events, partial methods can be used instead as a better performing and cleaner alternative. They are declared by creating a private method with an empty body and decorating it with the Partial keyword. The method may then be "re-implemented" elsewhere within its containing class. If the method is implemented, then the compiler will redirect all calls to the partial method to the implementing method. If the method is not implemented in its containing class, then the compiler silently removes any calls to it from the program. &lt;/p&gt;
&lt;p&gt;If we take a look at the generated LINQ to SQL Order class again you can see there is a region called "Extensibility Method Definitions" that contain Partial methods. There will be On...Changed and On...Changing partial methods here for each of the properties on the class. &lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;#Region&lt;/span&gt; &lt;span style="color: rgb(163,21,21)"&gt;"Extensibility Method Definitions"
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnLoaded()
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnValidate(action &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; System.Data.Linq.ChangeAction)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnCreated()
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnCustomerIDChanging(value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Integer&lt;/span&gt;)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnCustomerIDChanged()
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;.&lt;br&gt;.&lt;br&gt;.&lt;/pre&gt;
&lt;p&gt;Notice that the Changing and Changed methods are called at the appropriate times in each of the property setters. This allows us to write clean business rules on these properties in our partial class by defining the partial method. However if we don't write any code for these partial methods, the calls to them are removed from the IL. To access the partial class code open the model (dbml file) in the O/R designer, select a class, right-click on the class name and select "View Code". This will create a file named after your model where the partial class code that we write can reside. &lt;/p&gt;
&lt;p&gt;&lt;img src="http://blogs.msdn.com/photos/bethmassi/images/7896928/original.aspx"&gt;&lt;/p&gt;
&lt;p&gt;So to write some business rules for our classes in this example we're going to place code in the On...Changing methods and then implement IDataErrorInfo so that we can have the UI automatically display the validation messages. To make it easier to implement IDataErrorInfo on all our LINQ to SQL classes I'm going to create a base class that we can inherit from called BaseBusiness. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementing IDataErrorInfo&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;IDataErrorInfo requires us to implement only two properties, one called &lt;strong&gt;Error&lt;/strong&gt; and one default property &lt;strong&gt;Item&lt;/strong&gt; that both return a string. Error is used to describe what is wrong with the entire object. For instance, if we are displaying this object in a row of a DataGridView this property indicates what error message appears on the row header. If you use DataSets, this corresponds to the DataRow.RowError property. The Item property is used to determine the error message for a specific property (or column) that is passed in. In order to collect these messages for each property on our object (i.e. column in our table) we can use a generic dictionary of strings and either add or remove messages from the dictionary depending on the validation rules. Here's an example implementation of IDataErrorInfo on our base class:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Imports&lt;/span&gt; System.ComponentModel

&lt;span style="color: rgb(0,128,0)"&gt;''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' Base class for our LINQ to SQL classes.
''' This class demonstrates one way to implement the IDataErrorInfo
'''  interface so that the ErrorProvider and DataGridView can display
'''  validation errors in the UI.
''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; BaseBusiness
    &lt;span style="color: rgb(0,0,255)"&gt;Implements&lt;/span&gt; IDataErrorInfo

    &lt;span style="color: rgb(0,128,0)"&gt;'This dictionary contains a list of our validation errors for each field
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; validationErrors &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;New&lt;/span&gt; Dictionary(&lt;span style="color: rgb(0,0,255)"&gt;Of&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;)

    &lt;span style="color: rgb(0,0,255)"&gt;Protected&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; AddError(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; columnName &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; msg &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; validationErrors.ContainsKey(columnName) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            validationErrors.Add(columnName, msg)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Protected&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; RemoveError(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; columnName &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; validationErrors.ContainsKey(columnName) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            validationErrors.Remove(columnName)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Overridable&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;ReadOnly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt; HasErrors() &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Boolean
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; (validationErrors.Count &amp;gt; 0)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;ReadOnly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt; [Error]() &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt; _
        &lt;span style="color: rgb(0,0,255)"&gt;Implements&lt;/span&gt; System.ComponentModel.IDataErrorInfo.Error
        &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; validationErrors.Count &amp;gt; 0 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;.Format(&lt;span style="color: rgb(163,21,21)"&gt;"{0} data is invalid."&lt;/span&gt;, TypeName(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;))
            &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Nothing
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Default&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;ReadOnly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt; Item(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; columnName &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt;) &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;String&lt;/span&gt; _
        &lt;span style="color: rgb(0,0,255)"&gt;Implements&lt;/span&gt; System.ComponentModel.IDataErrorInfo.Item
        &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; validationErrors.ContainsKey(columnName) &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; validationErrors(columnName).ToString
            &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Nothing
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property
End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Next we need to inherit from this class in all our LINQ to SQL Classes. Back in our partial class file we can inherit all our LINQ to SQL classes from BaseBusiness:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; Customer
    &lt;span style="color: rgb(0,0,255)"&gt;Inherits&lt;/span&gt; BaseBusiness

&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class

Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; Product
    &lt;span style="color: rgb(0,0,255)"&gt;Inherits&lt;/span&gt; BaseBusiness

&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class

Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; OrderDetail
    &lt;span style="color: rgb(0,0,255)"&gt;Inherits&lt;/span&gt; BaseBusiness

&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class

Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; Order
    &lt;span style="color: rgb(0,0,255)"&gt;Inherits&lt;/span&gt; BaseBusiness

&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Writing the Business Rules&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we need to write our business rules into these classes. Let's take Order as an example. There are a couple rules I want to implement here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The OrderDate cannot be after the ShipDate&lt;/li&gt;
&lt;li&gt;The Customer must be specified&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;There are a couple places that we need to run these rules. One is when any of these fields change and the other is before the changes are submitted to the database. We need to handle both places because the user doesn't necessarily change all the properties and raise the On...Changing event. We need to handle the case when the user doesn't fill out a field and immediately attempts to save. This partial method is called OnValidate on our LINQ to SQL classes. This method is called automatically by the DataContext right before it attempts to submit the changes to the database when SubmitChanges is called. &lt;/p&gt;
&lt;p&gt;Because you have to run the rules in both of these situations it's probably easier to create a private method that checks each rule and call that method from both Partial On...Changing and OnValidate methods. If the rule is broken then we just call AddError to add the error message to the dictionary, otherwise we call RemoveError to remove it from the dictionary. Here's the code for my Order Partial class:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt; Order
    &lt;span style="color: rgb(0,0,255)"&gt;Inherits&lt;/span&gt; BaseBusiness

    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnCustomerIDChanging(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Integer&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckCustomerID(value)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnOrderDateChanging(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Date&lt;/span&gt;?)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckOrderDate(value)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnShipDateChanging(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Date&lt;/span&gt;?)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckShipDate(value)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OnValidate(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; action &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; System.Data.Linq.ChangeAction)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckCustomerID(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CustomerID)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckOrderDate(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDate)
        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.CheckShipDate(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDate)

        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Throw&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;New&lt;/span&gt; ValidationException(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.Error)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; CheckCustomerID(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Integer&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; value &amp;lt; 1 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.AddError(&lt;span style="color: rgb(163,21,21)"&gt;"CustomerID"&lt;/span&gt;, &lt;span style="color: rgb(163,21,21)"&gt;"Customer cannot be empty."&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.RemoveError(&lt;span style="color: rgb(163,21,21)"&gt;"CustomerID"&lt;/span&gt;)
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; CheckOrderDate(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Date&lt;/span&gt;?)
        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; value.HasValue &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; value &amp;gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.ShipDate &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.AddError(&lt;span style="color: rgb(163,21,21)"&gt;"OrderDate"&lt;/span&gt;, &lt;span style="color: rgb(163,21,21)"&gt;"Order date cannot be after the ship date."&lt;/span&gt;)
            &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.RemoveError(&lt;span style="color: rgb(163,21,21)"&gt;"OrderDate"&lt;/span&gt;)
            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; CheckShipDate(&lt;span style="color: rgb(0,0,255)"&gt;ByVal&lt;/span&gt; value &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Date&lt;/span&gt;?)
        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; value.HasValue &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; value &amp;lt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDate &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.AddError(&lt;span style="color: rgb(163,21,21)"&gt;"ShipDate"&lt;/span&gt;, &lt;span style="color: rgb(163,21,21)"&gt;"Ship date cannot be before the order date."&lt;/span&gt;)
            &lt;span style="color: rgb(0,0,255)"&gt;Else
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.RemoveError(&lt;span style="color: rgb(163,21,21)"&gt;"ShipDate"&lt;/span&gt;)
            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;Public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Overrides&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;ReadOnly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property&lt;/span&gt; HasErrors() &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Boolean
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;MyBase&lt;/span&gt;.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                &lt;span style="color: rgb(0,128,0)"&gt;'Returns True if any order details are invalid 
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Each&lt;/span&gt; detail &lt;span style="color: rgb(0,0,255)"&gt;In&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDetails
                    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; detail.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                        &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;True
&lt;/span&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Return&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;MyBase&lt;/span&gt;.HasErrors
        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Get
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Property

End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Class&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;So here we are calling our validation methods from the On...Changing as well as the OnValidate partial methods. If you throw an exception from OnValidate then that will halt the SubmitChanges from going further. I created my own ValidationException class that we can use to check from our save code on the form. I also included any Order Detail errors in the HasErrors property of this Order class. This means that if any order details have errors then this Order also reports that HasErrors is True. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Displaying Validation Errors in the UI&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now that we have our business rules implemented we can hook up the UI to display these messages. Objects that are being displayed in a DataGridView will automatically pick up our validation messages but for simple controls like textboxes we need to hook up an ErrorProvider. Just drag the ErrorProvider from the toolbox onto your form and then specify the BindingSource as the DataSource property, for this example it's the OrderBindingSource. That's it. The ErrorProvider will look for any error messages on our objects through the IDataErrorInfo interface. This occurs when a property changes. For instance, if I run the form now and change the Order Date to be after the Ship Date, an error will be displayed as I tab off of the Order Date:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://blogs.msdn.com/photos/bethmassi/images/7897237/original.aspx"&gt; &lt;/p&gt;
&lt;p&gt;In order to display the messages after validation fails when we attempt to submit changes to the database, we need to write some code to refresh the display. In this scenario we also need to check the OrderBindingSource's position to make sure that the user is sitting on the invalid Order so the visuals are clear as to what needs fixing. This is why I created my own exception class called ValidationException and added a property to our BaseBusiness class called HasErrors, so that we could easily handle this case.&lt;/p&gt;
&lt;p&gt;So back in our form we'll write the following Save code:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; OrderBindingNavigatorSaveItem_Click() _
    &lt;span style="color: rgb(0,0,255)"&gt;Handles&lt;/span&gt; OrderBindingNavigatorSaveItem.Click

    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.Validate()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.EndEdit()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDetailsBindingSource.EndEdit()

    &lt;span style="color: rgb(0,0,255)"&gt;Try
&lt;/span&gt;        db.SubmitChanges()

        MsgBox(&lt;span style="color: rgb(163,21,21)"&gt;"Your data was saved."&lt;/span&gt;)

    &lt;span style="color: rgb(0,0,255)"&gt;Catch&lt;/span&gt; ex &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; ValidationException&lt;br&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.DisplayErrors()
        MsgBox(&lt;span style="color: rgb(163,21,21)"&gt;"Please correct the errors on this form before saving."&lt;/span&gt;)

    &lt;span style="color: rgb(0,0,255)"&gt;Catch&lt;/span&gt; ex &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; Exception
        MsgBox(ex.ToString)
    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Try
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub

&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;summary&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' Displays any error information and navigates to the first error row
&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;/summary&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,128,0)"&gt;''' &lt;/span&gt;&lt;span style="color: rgb(128,128,128)"&gt;&amp;lt;remarks&amp;gt;&amp;lt;/remarks&amp;gt;
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;Private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt; DisplayErrors()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.ErrorProvider1.UpdateBinding()
    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderDetailsDataGridView.Refresh()&lt;/pre&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.Position &amp;gt; -1 &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; currentOrder &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; Order = &lt;span style="color: rgb(0,0,255)"&gt;CType&lt;/span&gt;(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.Current, Order)

        &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Not&lt;/span&gt; currentOrder.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;            &lt;span style="color: rgb(0,128,0)"&gt;'The error is not in view so navigate to it
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;For&lt;/span&gt; i = 0 &lt;span style="color: rgb(0,0,255)"&gt;To&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.Count - 1
                &lt;span style="color: rgb(0,0,255)"&gt;Dim&lt;/span&gt; order &lt;span style="color: rgb(0,0,255)"&gt;As&lt;/span&gt; Order = &lt;span style="color: rgb(0,0,255)"&gt;CType&lt;/span&gt;(&lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource(i), Order)
                &lt;span style="color: rgb(0,0,255)"&gt;If&lt;/span&gt; order.HasErrors &lt;span style="color: rgb(0,0,255)"&gt;Then
&lt;/span&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;Me&lt;/span&gt;.OrderBindingSource.Position = i
                    &lt;span style="color: rgb(0,0,255)"&gt;Exit&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub
&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;Next
&lt;/span&gt;        &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;    &lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;If
&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;End&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;Sub&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;To test this, add a new Order then navigate away from it. When you click Save the validation will fail and you will be positioned back on the order that failed. &lt;/p&gt;
&lt;p&gt;I placed the code for this article (including the previous article code on this topic) into &lt;a href="http://code.msdn.microsoft.com/formsoverlinq" target="_blank"&gt;a Code Gallery project&lt;/a&gt; for you to play with. &lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7897743" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>One-To-Many (Master-Detail) Forms with LINQ to SQL</title><link>http://blogs.msdn.com/bethmassi/archive/2008/02/19/one-to-many-master-detail-forms-with-linq-to-sql.aspx</link><pubDate>Wed, 20 Feb 2008 02:11:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7804901</guid><dc:creator>Beth Massi</dc:creator><slash:comments>61</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/7804901.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=7804901</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=7804901</wfw:comment><description>&lt;P&gt;In previous posts this month I showed how to use LINQ to SQL classes with a couple different Combobox data binding scenarios. (You can read those&amp;nbsp;articles &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx"&gt;here&lt;/A&gt; and &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx"&gt;here&lt;/A&gt;.)&amp;nbsp;Today I'm going to show you how to create a one-to-many data entry form (and we'll use a couple Combobox lookup lists as well). I'll show you what you need to do to enable proper insert, update and deletes of the hierarchical data. I'll also show how we can specify that these operations happen via stored procedures.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;The Database Model&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;For this example I'll be creating my own database and not using Northwind. This is because Northwind isn't a very typical database especially when it comes to referential integrity. I want to create tables that have non-nullable foreign keys as well as use a timestamp field for concurrency checking. &lt;/P&gt;
&lt;P&gt;So here's the database diagram of what we'll be building off of:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7797861/554x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7797861/554x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;I've also specified Update, Insert and Delete stored procedures for each of the tables. This is so we can lock down the database security a bit and disallow UPDATE, INSERT and DELETE SQL statements from executing against it. All we need to grant is SELECT and EXECUTE permissions. This is a typical configuration for databases because it helps prevent against malicious code executing on the database by stopping changes from happening outside the stored procs. &lt;/P&gt;
&lt;P&gt;So to get started building my form, I'll start by adding a new item to my project called "LINQ to SQL Classes" which will open the O/R designer and allow me to drag tables in my database from the Server Explorer onto the model's design surface. I'll drag all of the four tables above and since the database is called OMS I'll name the model OMS.dbml. This will create our LINQ to SQL classes and infer the associations from the database relationships. I'll also drag all the stored procs onto the Methods pane. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7799523/640x453.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7799523/640x453.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Next we want to associate the stored procs with the update, insert and delete behaviors for each class. Select the class then in the properties window you will see three properties; Delete, Insert and Update and they are all set to "Use Runtime". Select the properties and you then can specify the procedures in the methods pane to use for each behavior. You can also simply right-click on the class and select "Configure Behavior". On this screen you can specify the behavior for all the classes by selecting the Customize radio button and then selecting the corresponding procedure shown in the method pane. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7799742/557x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7799742/557x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;After you got all of these set up, save the model and this will generate all the LINQ to SQL classes plus the DataContext which is used to manage the connection and communication to our database. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Data Sources and Data Binding the Form&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Next we need to get these into our Data Sources window so that we can quickly get our form designed. I showed how to do this before in the previous posts against Northwind. This time I want to create a master-detail form of Orders and related OrderDetails in a grid and I want to show the Customer and Product as lookup lists. Select Add New DataSource from the Data menu and select Object as the Data Source Type. Next, select the Order class and click Finish. This will populate your Data Sources window with the Order and also its related OrderDetails because of the association. We'll also want to add Customer then Product to our Data Sources window as well because we'll need those for our lookup lists. &lt;/P&gt;
&lt;P&gt;When you inspect the properties of the classes in the Data Sources window you will see that the associated parent object is also visible along with the child collections. For instance, if you expand Order you will see the parent Customer as well as the child OrderDetails. This indicates the associated parent Customer object for that Order object. I'm going to want to display that information as a lookup list so change the drop control for Customer to "None" and change the CustomerID to a Combobox. I also do not want to display the Modified field so also set that to "None". Then I'll set the drop control of the Order to "Details".&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7800578/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7800578/original.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Drag the Order onto the form to set up the controls as well as the BindingNavigator and BindingSource for the Order. Next drag the Customer from the Data Sources Window onto the top of the CustomerID Combobox to set up the CustomerDataSource for the list of items. In the properties for the Combobox set the ValueMember = CustomerID and the DisplayMember = LastName in order to finish setting up the lookup list for Customer. &lt;/P&gt;
&lt;P&gt;Next drag the OrderDetails listed under Order onto the form to drop down a DataGridView. You will notice that this will also pull in the parent objects, Product and Order in this case. Just edit the columns and remove those as well as the Modified field. For this example, I'll still display but set the OrderDetailID and OrderID to ReadOnly since these will be filled in automatically for us after we save the data. We'll also need to change the ProductID column type to a DataGridViewComboBoxColumn and then select the Product as the DataSource by selecting Other Data Sources --&amp;gt; Project Data Sources --&amp;gt; Product. This process will create a ProductBindingSource in the component tray.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7801151/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7801151/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Also we will need to specify the DisplayMember = Name and ValueMember = ProductID on the column here.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Loading the LINQ to SQL Classes with Data&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now that we have our form designed and the data binding all set up we're ready to create our objects and fill them with data. Unlike when we are using DataSets on our forms, the Form Designer will not generate any loading or saving code for us when using LINQ to SQL classes. But the code we need to write is very straightforward and we can write LINQ queries to limit our result sets. For this simple example I will select all Orders and also all of the Customers and Products into our lookup lists but keep in mind this may be a bad design if there are hundreds of rows in your database. In that situation it's better to write a search form. &lt;/P&gt;
&lt;P&gt;So in the Form's Load event handler we need to set up the BindingSource's DataSources with data returned from our tables. First we'll fill the form with all the orders from the Orders table. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; Form1

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; OMSDataContext

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; Form1_Load() &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;MyBase&lt;/SPAN&gt;.Load

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.DataSource = db.Orders
&lt;/PRE&gt;
&lt;P&gt;Next we want to populate the Customers list. We can get cute and can specify a LINQ query here in order to select the customer names in "&lt;EM&gt;LastName, FirstName&lt;/EM&gt;" format.&lt;/P&gt;&lt;PRE class=code&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.CustomerBindingSource.DataSource = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; c &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; db.Customers _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Let&lt;/SPAN&gt; LastName = c.LastName &amp;amp; &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;", "&lt;/SPAN&gt; &amp;amp; c.FirstName _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Select&lt;/SPAN&gt; LastName, c.CustomerID _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;By&lt;/SPAN&gt; LastName&lt;/PRE&gt;
&lt;P&gt;Finally we want to select our list of Products. Here's a trick that will place an "empty" product at the top of the list so that it can indicate to the user to select a value. We can add validation later to check that the selection has been made by checking that the ProductID &amp;gt; 0.&lt;/P&gt;&lt;PRE class=code&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; emptyProduct &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Product() = _
                {&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; Product &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;With&lt;/SPAN&gt; {.Name = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"&amp;lt;Select a product&amp;gt;"&lt;/SPAN&gt;, .ProductID = 0}}

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.ProductBindingSource.DataSource = (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; Empty &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; emptyProduct).Union( _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; Product &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; db.Products _
                                              &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;By&lt;/SPAN&gt; Product.Name)

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;You might be wondering why we're not explicitly setting the OrderDetailBindingSource's DataSource. This is because the OrderDetails are loaded from the database automatically only when we access the OrderDetails collection on the Order object. This happens when the OrderBindingSource moves position and the OrderDetailsBindingSource needs to display the OrderDetail objects for the Order. If you want to see the T-SQL statements being run just put a call to db.Log = Console.Out in the Load to display the statements in the Debug Output window. Just make sure to remove it before building your release.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Saving Hierarchical Data&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Next we need to add the save code by enabling the save button and handling the click event. In my previous post when &lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx"&gt;we built a single-table entry form with a lookup list&lt;/A&gt; I showed how to do this:&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; OrderBindingNavigatorSaveItem_Click() _
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; OrderBindingNavigatorSaveItem.Click

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.Validate()
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.EndEdit()
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderDetailsBindingSource.EndEdit()

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;            db.SubmitChanges()

            MsgBox(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Your data was saved."&lt;/SPAN&gt;)

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Exception
           MsgBox(ex.ToString)&lt;/PRE&gt;&lt;PRE class=code&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;Okay so let's give this form a try. Run the form and make a change, and/or insert a new Order, click save, and you should see that everything worked out smoothly. The keys are properly populated on insert and the relationship works correctly. However if we try to delete an OrderDetail (child) row from our grid we get the following error:&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;System.InvalidOperationException: An attempt was made to remove a relationship between a Order and a OrderDetail. However, one of the relationship's foreign keys (OrderDetail.OrderID) cannot be set to null.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;To fix this we need to indicate to the model that we want to delete the OrderDetail when it's OrderID is set to null. Unfortunately this cannot be done in the O/R designer so you have to open the model in an XML editor. Fortunately, once you change it the designer won't mess with it again unless you remove the class completely. Open the dbml file with the XML Editor (just right-click on it an select "Open with...") and locate the XML that describes the OrderDetail class. Notice the association under the OrderDetail table:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Table&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;dbo.OrderDetail&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Member&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderDetails&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;gt;
  &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderDetail&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderDetailID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Int32&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Int NOT NULL IDENTITY&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;BR&gt;            &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsPrimaryKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsDbGenerated&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;false&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Int32&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Int NOT NULL&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;false&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ProductID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Int32&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Int NOT NULL&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;false&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Quantity&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Int32&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Int NOT NULL&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;false&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Price&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Decimal&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Money&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
    &amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Column&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Modified&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;System.Data.Linq.Binary&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DbType&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;rowversion NOT NULL&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;BR&gt;            &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;CanBeNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;false&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsVersion&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN style="BACKGROUND: yellow; mso-highlight: yellow"&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Association&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order_OrderDetail&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Member&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;ThisKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;BR&gt;                 &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsForeignKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"/&amp;gt;&lt;BR&gt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;    &amp;lt;Association&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Product_OrderDetail&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Member&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Product&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;ThisKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ProductID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;BR&gt;                 &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Product&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsForeignKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; /&amp;gt;
  &amp;lt;/&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;We need to add an attribute here called DeleteOnNull and set it to true in order to be able to delete a child row independently in the database when calling SubmitChanges(). Once we make this change we can now delete just a single OrderDetail from the grid and save normally: &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&amp;lt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;Association&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Name&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order_OrderDetail&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Member&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;ThisKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;OrderID&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;Type&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Order&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt; &lt;BR&gt;             &lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;IsForeignKey&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;" &lt;SPAN style="BACKGROUND: yellow; mso-highlight: yellow"&gt;&lt;SPAN style="COLOR: rgb(255,0,0)"&gt;DeleteOnNull&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;=&lt;/SPAN&gt;"&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;true&lt;/SPAN&gt;"/&amp;gt;&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;The other option to fix this issue is to modify the Delete Rule to "Cascade" on the relationship in the database. In that case the designer correctly infers this attribute on the association. 
&lt;P&gt;Okay let's run the form again and now when you try to delete an OrderDetail child from the grid and click save, it saves without error. But if you try to delete an entire order by clicking the delete button on the ToolStrip and then save we now get a database error: 
&lt;P&gt;&lt;EM&gt;System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_OrderDetail_Orders". The conflict occurred in database "OMS", table "dbo.OrderDetail", column 'OrderID'.&lt;BR&gt;The statement has been terminated.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;This is because unlike DataSets, you can't specify in the model that when you delete a parent, it should cascade to the children automatically. So we need to write some code to do this. There's a variety of ways you can do this and one way I already showed in a &lt;A href="http://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx"&gt;previous post&lt;/A&gt; by adding code to the DataContext that works nicely if we are not using stored procs. The basic idea is that we need to tell the DataContext to delete the child objects anytime an Order is deleted. In this example, I chose to do this by calling DeleteOnSubmit before we call SubmitChanges. I added this code into the Click event handler for the Delete button on the ToolStrip of the form:&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; BindingNavigatorDeleteItem_Click() _
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; BindingNavigatorDeleteItem.Click

        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Position &amp;gt; -1 &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Then&lt;BR&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;            'Grab a reference to the currently selected order&lt;/SPAN&gt;
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; order &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Order = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.OrderBindingSource.Current, Order)
            &lt;BR&gt;            &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;'Ensure that children are deleted when the parent is deleted
&lt;/SPAN&gt;            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Each&lt;/SPAN&gt; detail &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; order.OrderDetails
                db.OrderDetails.DeleteOnSubmit(detail)
            &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Next
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;If
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; &lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Now run the form and try a variety of Update, Insert and Delete operations on the data and you will have a smooth ride. If you enable logging on the DataContext or run SQL profiler on the database you will see our stored procedures being called in the proper order.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7804786/511x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7804786/511x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Next time I'll show you how we can add simple validation to our LINQ to SQL classes by creating a base business class to inherit from and using the IDataErrorInfo interface along with the ErrorProvider.&lt;/P&gt;
&lt;P&gt;UPDATE: I placed the code for this article (including the previous article code on this topic) into &lt;A href="http://code.msdn.microsoft.com/formsoverlinq" target=_blank&gt;a Code Gallery project&lt;/A&gt; for you to play with. &lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7804901" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Creating Lookup Lists with LINQ to SQL</title><link>http://blogs.msdn.com/bethmassi/archive/2008/02/07/creating-lookup-lists-with-linq-to-sql.aspx</link><pubDate>Thu, 07 Feb 2008 20:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7503956</guid><dc:creator>Beth Massi</dc:creator><slash:comments>23</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/7503956.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=7503956</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=7503956</wfw:comment><description>&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx"&gt;In yesterday's post&lt;/A&gt; I showed you how to bind LINQ to SQL classes to a Combobox in order to filter records on a one-to-many form. Today I want to show you how how you can use a Combobox as a lookup list in order to edit values on a record. We'll be building on yesterday's example. So to recap, we have added a LINQ to SQL object model called &lt;A href="http://blogs.msdn.com/photos/bethmassi/images/7495818/original.aspx" target=_blank mce_href="http://blogs.msdn.com/photos/bethmassi/images/7495818/original.aspx"&gt;Northwind.dbml&lt;/A&gt; to our Windows Forms application that contains Regions and Territory classes and then &lt;A href="http://blogs.msdn.com/photos/bethmassi/images/7496216/original.aspx" target=_blank mce_href="http://blogs.msdn.com/photos/bethmassi/images/7496216/original.aspx"&gt;added the Region object to the Datasources Window&lt;/A&gt; by selecting "Add New DataSource" and selecting the &lt;A href="http://blogs.msdn.com/photos/bethmassi/images/7496168/original.aspx" target=_blank mce_href="http://blogs.msdn.com/photos/bethmassi/images/7496168/original.aspx"&gt;Object data source type&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;Let's create a simple form that we can use to edit Territories. From the Data Sources window, expand the Region and its related Territories collection then drag the TerritoryID and TerritoryDescription onto the form as Textboxes. This will automatically create the TerriroriesBindingSource and BindingNavigator. Next, select the Combobox control for the RegionID and then drag that onto the form.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7503500/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7503500/original.aspx"&gt;&amp;nbsp; &lt;/P&gt;
&lt;P&gt;This sets up the binding to the Territory so that when a value is selected in the Combobox, the Territory.RegionID property is set to that value. Next we need to bind the list of Regions to display in the Combobox, so just drag the Region object (root table displayed in the Data Sources Window) onto the top of (directly over) the Combobox you just dropped on the form. This is a handy trick that will set up the RegionBindingSource in the component tray and set the Combobox's DataSource property to the RegionBindingSource. Next set the ValueMember of the Combobox to "RegionID" in the properties sheet. You may also want to set the Combobox DropDownStyle to "DropDownList".&lt;/P&gt;
&lt;P&gt;If we were to look at the generated code, this is how it sets up our data binding in this scenario:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionIDComboBox.DataBindings.Add(&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; System.Windows.Forms.Binding(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"SelectedValue"&lt;/SPAN&gt;, _
                                     &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource, &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"RegionID"&lt;/SPAN&gt;, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;True&lt;/SPAN&gt;))
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionIDComboBox.DataSource = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionIDComboBox.DisplayMember = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"RegionDescription"
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionIDComboBox.ValueMember = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"RegionID"&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;What this is doing is first setting up the data binding between the selected value of the Combobox and the TerritoriesBindingSource on the RegionID property. Then we set the DataSource property of the Combobox to the RegionBindingSource which fills the list with Regions to choose from. &lt;/P&gt;
&lt;P&gt;Now the rest is up to us. Unlike when using DataSets, the designer doesn't generate code to fill or save our data, but fortunately the code we have to write is minimal. First let's fill our data. It's important in this scenario to fill the Territories before the Regions so that the data binding will work properly. &lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; Form2

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; NorthwindDataContext

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt;()

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' This call is required by the Windows Form Designer.
&lt;/SPAN&gt;        InitializeComponent()

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' Add any initialization after the InitializeComponent() call.
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource.DataSource = db.Territories
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource.DataSource = db.Regions
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Next we need to write our save code. It's really easy to do this when working with client-server forms like this one because the DataContext automatically tracks changes to our objects for us. We just call SubmitChanges on the DataContext when we want to send all the changes to the database. Enable the save button on the BindingNavigator and then write some simple Save code in the click event handler:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; TerritoriesBindingNavigatorSaveItem_Click() _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Handles&lt;/SPAN&gt; TerritoriesBindingNavigatorSaveItem.Click

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource.EndEdit()

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try
&lt;/SPAN&gt;        db.SubmitChanges()

        MsgBox(&lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Saved."&lt;/SPAN&gt;)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Catch&lt;/SPAN&gt; ex &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; Exception
        MsgBox(ex.ToString)
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Try

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Let's give it a try! Run the form and edit, add and delete the Territories then click Save and you should see your changes next time you run the form. If you want to see the T-SQL statements being run just put a call to db.Log = Console.Out in order to display the statements in the Debug Output window. Just make sure to remove it before building your release.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7503689/640x471.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7503689/640x471.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Hopefully this helps get you started designing forms using LINQ to SQL classes. It's interesting to note that I actually didn't write one LINQ query in this example. However it's really easy to write queries against your object model now and have the LINQ to SQL provider worry about the SQL statements. For instance, if I wanted to only return the Territories in the Region that started with "East" then we could set the TerritoriesBindingSource.DataSource property like so:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource.DataSource = _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;From&lt;/SPAN&gt; Territory &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;In&lt;/SPAN&gt; db.Territories _
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Where&lt;/SPAN&gt; Territory.Region.RegionDescription &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Like&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"East*"

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource.DataSource = db.Regions

&lt;/PRE&gt;
&lt;P&gt;This would result in only a subset of data returned to our form which is always a good idea especially in multi-user database systems! Here's the SQL statements that are automatically sent to the database for us:&lt;/P&gt;&lt;PRE class=code&gt;SELECT [t0].[TerritoryID], [t0].[TerritoryDescription], [t0].[RegionID]
FROM [dbo].[Territories] AS [t0]
INNER JOIN [dbo].[Region] AS [t1] ON [t1].[RegionID] = [t0].[RegionID]
WHERE [t1].[RegionDescription] LIKE @p0
-- @p0: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [East%]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8

SELECT [t0].[RegionID], [t0].[RegionDescription]
FROM [dbo].[Region] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Looks like reasonable SQL code to me! ;-) &lt;/P&gt;
&lt;P&gt;Next time we'll create a one-to-many data entry form and talk about a couple tricks you'll need to know in order to update data properly. (Psssst... &lt;A href="http://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx"&gt;here's a hint&lt;/A&gt;) &lt;/P&gt;
&lt;P&gt;UPDATE: I placed the code for&amp;nbsp;all the articles on this topic&amp;nbsp;into &lt;A href="http://code.msdn.microsoft.com/formsoverlinq" target=_blank&gt;a Code Gallery project&lt;/A&gt; for you to play with. &lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7503956" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Related Data Binding and ComboBoxes with LINQ to SQL</title><link>http://blogs.msdn.com/bethmassi/archive/2008/02/06/related-data-binding-and-comboboxes-with-linq-to-sql.aspx</link><pubDate>Thu, 07 Feb 2008 01:07:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7499422</guid><dc:creator>Beth Massi</dc:creator><slash:comments>35</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/7499422.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=7499422</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=7499422</wfw:comment><description>&lt;P&gt;&lt;A href="http://blogs.msdn.com/bethmassi/archive/2007/04/25/tips-on-related-data-binding-and-comboboxes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/04/25/tips-on-related-data-binding-and-comboboxes.aspx"&gt;In a previous post&lt;/A&gt; I showed how to set up related data binding using ComboBoxes against DataSets and a loyal reader asked how this would be done using LINQ and Visual Studio 2008. I assume he meant LINQ to SQL in this case, because remember LINQ can be used over DataSets too -- you just have to call the TableAdapter fill methods like normal and then you can write queries over the in-memory DataSets &lt;A href="http://msdn2.microsoft.com/en-us/vbasic/bb737877.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb737877.aspx"&gt;like I show in this video&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;With LINQ to SQL this can be easier to do and more efficient depending on how you set it up. This is because LINQ to SQL has the ability to fetch data from SQL Server only as you need it automatically for you. With the DataSet this is a more manual process of calling the TableAdapter's Fill method.&lt;/P&gt;
&lt;P&gt;In this first example I'm going to set up a ComboBox that is used as a filter to select records to display in a grid. So we want to display a list of parent table's records and use that as a filter to display related child records. For instance, as the user selects a record in the ComboBox, we want to display all the related child records in a grid below.&lt;/P&gt;
&lt;P&gt;The first thing is to create a new Windows Forms application and I'll call it LINQComboBinding. Next, we need to add a LINQ to SQL data model that maps 1:1 to our database tables. This is a new item in Visual Studio 2008 that automatically generates classes for our tables which allows us to perform LINQ queries in our code and have them automatically translated and sent as T-SQL statements to SQL-Server. The advantage to using LINQ to SQL classes is that now you can have compile-time checking on your queries and access the results as strongly-typed objects. To add LINQ to SQL classes, right-click on the Project and select "Add New Item" and then choose the LINQ to SQL classes template. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7495697/640x380.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7495697/640x380.aspx"&gt; &lt;/P&gt;
&lt;P&gt;I'll be using the Northwind sample database in this example so I'll name the model Northwind.dbml. Click "Add" and this will open the O/R designer. Just like the DataSet designer, you can drag tables from your Server Explorer (or Database Explorer if using VS2008 Express edition) onto the surface of the designer in order to automatically generate code that defines your LINQ to SQL classes. I'll select Region and Territories for this example.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7495818/640x344.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7495818/640x344.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Notice that the O\R designer infers the associations of the classes it creates by reading the database relationships. In the above diagram, the Region class will contain a collection of Territory classes called Territories. You can take a look at the code it generates by showing all files on the project and then opening the Northwind.designer.vb file. &lt;/P&gt;
&lt;P&gt;Okay now we're ready to start designing our form. When we work with data binding on Windows forms we use the Data Sources Window to drag items onto the form designer to create and bind our controls. Under the covers all the controls are bound to a BindingSource that manages the data binding between&amp;nbsp;the control and the DataSource. I've covered this all before with respect to using DataSets as the DataSource of the BindingSource here &lt;A href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx?wt.slv=topsectionsee" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx?wt.slv=topsectionsee"&gt;in this video&lt;/A&gt; as well as the &lt;A href="http://blogs.msdn.com/bethmassi/archive/2007/04/25/tips-on-related-data-binding-and-comboboxes.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/04/25/tips-on-related-data-binding-and-comboboxes.aspx"&gt;previous article on this topic&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;What's great about the BindingSource is that it can manage data binding between the control and any type of DataSource, including collections of objects, not just DataSets. And we can use the same familiar drag-drop from the Data Sources Window even with LINQ to SQL classes, there's just a couple of manual steps. &lt;/P&gt;
&lt;P&gt;When we open the Data Sources Window (from the menu select Data --&amp;gt; Show Data Sources) you'll see that our LINQ to SQL classes do not show up automatically like our DataSets do. But all we need to do is "Add New DataSource" and then select Object as the Data Source Type in the Data Source Configuration Wizard:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7496168/567x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7496168/567x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Next thing we do is select the object we want to bind to. In this case, all we need to select is the Region class because this class already contains the Territories collection which we will use on our one-to-many form.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7496216/622x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7496216/622x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Click Next then Finish and this will populate your Data Sources window with Region and you will see the Territories collection under that. For this example we're going to want a Combobox that is used as a filter for a grid that is displaying the related Territories. In the Data Sources window with the Form Designer open, set the Region to drop a Combobox control. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7497508/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7497508/original.aspx"&gt; &lt;/P&gt;
&lt;P&gt;If you only see DataGridView and Details listed then select "Customize..." and check the Combobox as an available control for the List type. Next drag the Region Combobox onto the form. This will automatically create the RegionBindingSource and a RegionBindingNavigator. I'm going to delete the BindingNavigator because we're going to use the Combobox as a navigator. Next drag the related Terrirories DataGridView onto the form to automatically set up the related TerritoryBindingSource. The DataGridView will also pick up the Region property on the Territory class which is a reference back to the parent Region object, just edit the columns on the DataGridView through its smart tag and remove it. &lt;/P&gt;
&lt;P&gt;When we drag the Region Combobox onto our form the designer generates the proper code to set up the ValueMember and DisplayMember properties on the Combobox. ValueMember doesn't actually matter in this case. It also sets up the related TerriroryBindingSource. The key to setting up related BindingSources (even when using DataSets) is that the child's BindingSource's DataSource property must be set to the parent BindingSource and the child's DataMember property is set to the name of the "relation", in the case of a DataSet this would be the DataRelation, in this case however, it's the name of the child collection "Territories". If we were to set this up in code ourselves it would look like this:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionComboBox.DataSource = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource
&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionComboBox.DisplayMember = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"RegionDescription"

&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource.DataMember = &lt;SPAN style="COLOR: rgb(163,21,21)"&gt;"Territories"
&lt;/SPAN&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.TerritoriesBindingSource.DataSource = &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;When we use the Data Sources window this is all set up automatically in the designer-generated code. The only thing the designer doesn't do for us is fill the data like it does when using DataSets. It's very straightforward. In the code-behind for the form add the following code:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Public&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt; Form1

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Dim&lt;/SPAN&gt; db &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt; NorthwindDataContext

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;New&lt;/SPAN&gt;()

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' This call is required by the Windows Form Designer.
&lt;/SPAN&gt;        InitializeComponent()

        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;' Add any initialization after the InitializeComponent() call.
&lt;/SPAN&gt;        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Me&lt;/SPAN&gt;.RegionBindingSource.DataSource = db.Regions
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Sub

End&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;Class&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;The NorthwindDataContext is a class that was generated when we created our model Northwind.dbml. This manages the connection between our LINQ to SQL classes and the database. It also keeps track of changes we make to the Region and Territory objects. All we need to do is set the RegionBindingSource.DataSource property equal to the Regions property on the DataContext. This will initiate the load of all the regions from the database and will create a collection of Region objects. Notice that we aren't specifically loading any territories. By default, LINQ to SQL classes will lazy load related collections so only the related Territory objects will load from the database when the selected Region object's Territories collection is accessed. This happens when we move the position of the RegionBindingSource and the TerritoryBindingSource filters its list. You can see what I mean by adding this code before you set the RegionBindingSource.DataSource property in order to log the T-SQL statements to Debug Output window:&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(0,128,0)"&gt;&lt;FONT color=#808080&gt;' Add any initialization after the InitializeComponent() call.&lt;/FONT&gt;
&lt;/SPAN&gt;&lt;STRONG&gt;db.Log = Console.Out
&lt;/STRONG&gt;&lt;FONT color=#808080&gt;Me.RegionBindingSource.DataSource = db.Regions&lt;/FONT&gt;&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;Run the form. When it opens you will see two statements, one to retrieve all the Regions and one to just load the Territories related to the selected Region. As you select a Region in the Combobox, just the related Territories are selected and displayed. &lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/7498529/471x480.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/7498529/471x480.aspx"&gt; &lt;/P&gt;
&lt;P&gt;Voila! Notice that in VS 2008 the Combobox will automatically move the BindingSource's position similar to other list controls like the ListBox and the DataGridView eliminating the need to handle the SelectedIndexChanged event to set the BindingSource.Position manually. &lt;/P&gt;
&lt;P&gt;In the next post I'll show how we can use the Region Combobox as a lookup list when editing Territories and how we can submit changes to the database. &lt;/P&gt;
&lt;P&gt;UPDATE: I placed the code for&amp;nbsp;all the articles on this topic&amp;nbsp;into &lt;A href="http://code.msdn.microsoft.com/formsoverlinq" target=_blank&gt;a Code Gallery project&lt;/A&gt; for you to play with. &lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7499422" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/LINQ/default.aspx">LINQ</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2008/default.aspx">VS2008</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Using Data Across Multiple Windows Forms</title><link>http://blogs.msdn.com/bethmassi/archive/2007/10/01/using-data-across-multiple-windows-forms.aspx</link><pubDate>Mon, 01 Oct 2007 21:11:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5226466</guid><dc:creator>Beth Massi</dc:creator><slash:comments>31</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/5226466.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=5226466</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=5226466</wfw:comment><description>&lt;P&gt;Recently I've had more than a few questions about how to handle working with data across multiple forms. If you've watched my &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx?wt.slv=topsectionimg#formsoverdata" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx?wt.slv=topsectionimg#formsoverdata"&gt;Forms over Data video series&lt;/A&gt; you know how to &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb643825.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb643825.aspx"&gt;create a database&lt;/A&gt;, &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb643826.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb643826.aspx"&gt;connect to it&lt;/A&gt;, &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb725826.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb725826.aspx"&gt;save your data properly&lt;/A&gt;, and &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx"&gt;work with data in your code&lt;/A&gt;. In this post I'll expand upon those videos and walk through an example of how to create multiple forms that work against the same DataSet. We'll use the Northwind Categories and Products for this example.&lt;/P&gt;
&lt;P&gt;There are actually many ways to connect multiple forms to the same&amp;nbsp;DataSet depending on your scenario, but I'll take a common example of displaying editable&amp;nbsp;detail forms from a main form&amp;nbsp;with a grid-style edit like this:&amp;nbsp;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/5243039/500x361.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/5243039/500x361.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Here we've got a form with two DataGridViews displaying Categories and their related Products. Once you connect to the Northwind Database with the Data Sources Window, you can choose Categories and Products tables and that will create a DataSet and the corresponding TableAdapters for you just like I showed in the &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb643827.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb643827.aspx"&gt;One-to-Many video&lt;/A&gt;. Once your DataSet is set up then you can drag and drop the Categories and related Products tables from the Data Sources Window to your form. This will set up the two BindingSources and bind the grids properly as well as add a BindingNavigator ToolStrip to the form. Now what we want to do is create a second form that allows us to edit the product details. &lt;/P&gt;
&lt;P&gt;First I added a couple lines of code to the TableNewRow event handler on the Categories and Products DataTables so that it would be easier working with the data. In this handler I just set some defaults for the non-nullable fields. To get to the code behind file for your DataSet just right-click on the&amp;nbsp;DataSet in the Solution Explorer&amp;nbsp;and select "View Code". This is where you can add simple validation as well like I showed in the &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb643821.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb643821.aspx"&gt;video on adding validation&lt;/A&gt;.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Partial&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;/SPAN&gt; CategoriesProductsDataSet&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Partial&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;/SPAN&gt; CategoriesDataTable&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; CategoriesDataTable_TableNewRow(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Object&lt;/SPAN&gt;, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-tab-count: 1"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.Data.DataTableNewRowEventArgs) _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Handles&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.TableNewRow&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;'Set defaults for non-nullable fields&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; category &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; CategoriesRow = &lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(e.Row, CategoriesRow)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;category.CategoryName = &lt;SPAN style="COLOR: #a31515"&gt;"New Category"&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Partial&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;/SPAN&gt; ProductsDataTable&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Private&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; ProductsDataTable_TableNewRow(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Object&lt;/SPAN&gt;, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.Data.DataTableNewRowEventArgs) _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Handles&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.TableNewRow&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 1in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;'Set defaults for non-nullable fields&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; product &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; ProductsRow = &lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(e.Row, ProductsRow)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;product.ProductName = &lt;SPAN style="COLOR: #a31515"&gt;"New Product"&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;product.Discontinued = &lt;SPAN style="COLOR: blue"&gt;False&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Class&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;Next I added a new form to the project and again used the Data Sources Window to drag and drop the Product fields I want to edit onto the new form. This time, however, I don't need the BindingNavigator ToolStrip so I just selected that and deleted it and instead added two buttons on the bottom of the form for OK and Cancel. &lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/5226609/500x293.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/5226609/500x293.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Next I deleted ProductsTableAdapter in the component tray and then opened up the code-behind of the detail form. I then deleted the Form.Load handler with the Fill code that was auto-generated for us. This is because we do not want to re-fill the DataSet from the database, instead we are going to pass the data to this form from our main form. &lt;/P&gt;
&lt;P&gt;The easiest way to pass the data in this situation is to create a new constructor that accepts the DataSet we're editing on the&amp;nbsp;main form and the primary key of the product we want to edit. Then we can&amp;nbsp;set the ProductBindingSource's DataSource&amp;nbsp;and Filter property on the detail form so that the&amp;nbsp;it displays&amp;nbsp;the selected&amp;nbsp;row. This keeps the main form and the detail form edits in sync.&amp;nbsp;In the details form code right under the class definition type "Sub New" and hit enter to auto-generate the correct constructor call, then change the signature and set the ProductBindingSource properties.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;Sub&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt;(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; ds &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; CategoriesProductsDataSet, &lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; id &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;' This call is required by the Windows Form Designer.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;InitializeComponent()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;' Add any initialization after the InitializeComponent() call.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;' Set the DataSource of the BindingSource and then set the Filter&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;'&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;so that the correct row will be displayed on the detail form.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.DataSource = ds&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.Filter = &lt;SPAN style="COLOR: #a31515"&gt;"ProductID = "&lt;/SPAN&gt; &amp;amp; id.ToString&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes; mso-bidi-font-size: 10.0pt"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 9pt; LINE-HEIGHT: 115%; mso-bidi-font-size: 11.0pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;Then back in the main form I created a button on the ToolStrip that opens this details form. I call ProductsBindingSource.EndEdit() first so that any changes made on the main form are pushed into the DataSet before we attempt to edit the row on the detail form. Then I get the current Product row by casting the ProductBindingSource.Current property to ProductsRow and pass the ProductID to the detail form's constructor along with the reference to the CategoriesProductsDataSet on the main form:&lt;/P&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Private&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt;&lt;FONT color=#000000&gt; ToolStripButton1_Click(&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt;&lt;FONT color=#000000&gt; sender &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; System.Object, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; e &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; System.EventArgs) _&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Handles&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; ToolStripButton1.Click&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&lt;FONT color=#000000&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&lt;FONT color=#000000&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;&lt;FONT color=#000000&gt;.ProductsBindingSource.EndEdit()&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&lt;FONT color=#000000&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;If&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;&lt;FONT color=#000000&gt;.ProductsBindingSource.Position &amp;gt; -1 &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Then&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN style="COLOR: green"&gt;'Get the current product row&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt;&lt;FONT color=#000000&gt; row &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; CategoriesProductsDataSet.ProductsRow&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;row = &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;&lt;FONT color=#000000&gt;(&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;&lt;FONT color=#000000&gt;(&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;&lt;FONT color=#000000&gt;.ProductsBindingSource.Current, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;DataRowView).Row, CategoriesProductsDataSet.ProductsRow)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&lt;FONT color=#000000&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN style="COLOR: green"&gt;'Open the product detail form passing the dataset and the product ID&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt;&lt;FONT color=#000000&gt; frm &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt;&lt;FONT color=#000000&gt; Form2(&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;&lt;FONT color=#000000&gt;.CategoriesProductsDataSet, row.ProductID)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&lt;/SPAN&gt;frm.Show()&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P mce_keep="true"&gt;You can open the details form modal or modeless. Here I'm allowing the user to open as many detail forms as they want but if you want to only open one at a time then just change the frm.Show() line to frm.ShowDialog() instead. This example demonstrates editing the current row and does nothing if there is no product row selected. You could optionally add a call to ProductBindingSource.AddNew() before the EndEdit() call in this scenario if you need to add a row programmatically before the form is opened. (NOTE: It's important that you add defaults for non-nullable fields like I showed above in the DataTable partial classes&amp;nbsp;if you call AddNew() then EndEdit() in succession.)&lt;/P&gt;
&lt;P mce_keep="true"&gt;The last bit of code is on the detail form that calls EndEdit() or CancelEdit() on the ProductsBindingSource depending on if they clicked Cancel or OK on the detail form, or just closed the form without selecting OK. This will either accept or discard the changes made to the product row on the particular detail form.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Private&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; Form2_FormClosing(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Object&lt;/SPAN&gt;, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.Windows.Forms.FormClosingEventArgs) _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Handles&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.FormClosing&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.CancelEdit()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Private&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; cmdCancel_Click(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.Object, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.EventArgs) &lt;SPAN style="COLOR: blue"&gt;Handles&lt;/SPAN&gt; cmdCancel.Click&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.CancelEdit()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.Close()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Private&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; cmdOK_Click(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.Object, _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.EventArgs) &lt;SPAN style="COLOR: blue"&gt;Handles&lt;/SPAN&gt; cmdOK.Click&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductsBindingSource.EndEdit()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.Close()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;
&lt;P mce_keep="true"&gt;I've&lt;A class="" href="http://blogs.msdn.com/bethmassi/attachment/5226466.ashx" mce_href="http://blogs.msdn.com/bethmassi/attachment/5226466.ashx"&gt; attached a complete example to this post&lt;/A&gt; that works against the Northwind database. It also shows how to properly save related DataTables like I demonstrated in &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb725826.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb725826.aspx"&gt;this video&lt;/A&gt; and &lt;A class="" href="http://blogs.msdn.com/bethmassi/archive/2007/07/10/working-with-tableadapters-related-datatables-and-transactions.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/07/10/working-with-tableadapters-related-datatables-and-transactions.aspx"&gt;this post&lt;/A&gt;. Remember the key to working with data on your forms is to&amp;nbsp;use the BindingSource object as this class really makes working with data and form controls very simple. For more information see the &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb725824.aspx"&gt;Understanding Data video&lt;/A&gt;. &lt;/P&gt;
&lt;P mce_keep="true"&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5226466" width="1" height="1"&gt;</description><enclosure url="http://blogs.msdn.com/bethmassi/attachment/5226466.ashx" length="40279" type="application/x-zip-compressed" /><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2005/default.aspx">VS2005</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Binding Multiple Combo Boxes to the Same Data Source</title><link>http://blogs.msdn.com/bethmassi/archive/2007/09/19/binding-multiple-comboboxes-to-the-same-datasource.aspx</link><pubDate>Wed, 19 Sep 2007 21:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:5001362</guid><dc:creator>Beth Massi</dc:creator><slash:comments>8</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/5001362.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=5001362</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=5001362</wfw:comment><description>&lt;P&gt;Recently the &lt;A class="" href="http://blogs.msdn.com/vbteam/" target=_blank mce_href="http://blogs.msdn.com/vbteam/"&gt;VB Team&lt;/A&gt;&amp;nbsp;received a customer bug submitted through &lt;A class="" href="http://connect.microsoft.com/" target=_blank mce_href="http://connect.microsoft.com/"&gt;Connect&lt;/A&gt; that had to do with binding multiple combo boxes to the same data source. The customer was reporting that once a selection was made in one combo, the other combos were also changing. The resolution was "By Design" because of the way the customer was setting up the data binding.&amp;nbsp;We thought it would benefit the community if I posted information on how we helped the customer resolve the issue, so let's talk about what was happening and the solution. But&amp;nbsp;first some architectural background on data binding.&lt;/P&gt;
&lt;P&gt;Data binding on Windows Forms involves a few different objects depending on where your data is coming from (i.e.&amp;nbsp;an object or the database directly) but we're going to focus on the database scenario. The TableAdapter is responsible for talking to the database and pulling in result sets into DataTables contained in a DataSet and for sending back the updates -- this is referred to sometimes as CRUD -- Create, Retrieve, Update, Delete.&amp;nbsp;The DataSet maintains the client-side representation of that data (and can also perform validation) by placing the data in DataRow&amp;nbsp;objects that are contained in DataTables.&amp;nbsp;The BindingSource is responsible for maintaining&amp;nbsp;currency, that's the position of the DataRow in the DataTable in which the controls are displaying data from.&amp;nbsp;Here's a simple diagram pulled from my &lt;A class="" href="http://msdn2.microsoft.com/en-au/vbasic/bb466226.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-au/vbasic/bb466226.aspx"&gt;How Do I video&lt;/A&gt; on &lt;A class="" href="http://msdn2.microsoft.com/en-au/vbasic/bb725824.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-au/vbasic/bb725824.aspx"&gt;Understanding Data&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;&lt;IMG style="WIDTH: 500px; HEIGHT: 279px" height=279 src="http://blogs.msdn.com/photos/bethmassi/images/4999591/500x279.aspx" width=500 mce_src="http://blogs.msdn.com/photos/bethmassi/images/4999591/500x279.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Once you get your DataSet and TableAdapters set up you're ready to start data binding to your controls. You use the Data Sources Window to drag fields and tables onto your form and I've shown how to do this in &lt;A class="" href="http://msdn2.microsoft.com/en-au/vbasic/bb466226.aspx?wt.slv=topsectionimg#formsoverdata" target=_blank mce_href="http://msdn2.microsoft.com/en-au/vbasic/bb466226.aspx?wt.slv=topsectionimg#formsoverdata"&gt;many videos in the Forms Over Data series&lt;/A&gt;. When you do this, the DataSet and TableAdapters are dropped onto your form and then a new BindingSource object is created for you. It's this BindingSource that is providing the "glue" between the DataSet and your controls.&lt;/P&gt;
&lt;P&gt;There's one video in particular that shows you &lt;A class="" href="http://msdn2.microsoft.com/en-au/vbasic/bb643829.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-au/vbasic/bb643829.aspx"&gt;how to create lookup lists&lt;/A&gt;. In this scenario, the data binding is a bit more complex. When you bind a textbox to a BindingSource it's a simple binding to a single field in the current row. When you bind a combo box that acts as a lookup list however, you need two binding sources because you need to be able to display the data from one lookup table and place the value into the table that you're editing. &lt;/P&gt;
&lt;P&gt;It's very easy to set this up through the Data Sources Window. First drag all the fields you want to edit onto the from from your edit table by selecting the drop down next to the table and choosing "Details". You can change the lookup&amp;nbsp;fields to combo boxes by selecting the drop down next to the fields and changing the control to a combo box:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/5001195/original.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/5001195/original.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Once you have the controls bound to your edit table you can easily&amp;nbsp;bind the combo box&amp;nbsp;list&amp;nbsp;with data from your lookup table. Just drag the lookup table from the Data Sources Window directly over the combo box. Repeat this process for each combo box you want to display that lookup data. Then use the property window or the smart tags on the combo boxes to adjust the ValueMember (the field who's value you want placed in your edit table)&amp;nbsp;and DisplayMember (the field who's value you want displayed in the combo) properties.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/5001339/500x308.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/5001339/500x308.aspx"&gt;&lt;/P&gt;
&lt;P&gt;This will create THREE separate LookupBindingSources, (four total BindingSources once you count the edit table's BindingSource). &lt;STRONG&gt;This is the key to binding the same lookup data to multiple combo box controls&lt;/STRONG&gt;. If you use the same LookupBindingSource instance for the combo boxes then you will end up in the same situation as the customer who reported the bug. If you don't use separate BindingSource instances then as a selection is made in one combo, the others also change to that value, and that's probably not the desired behavior you want. &lt;/P&gt;
&lt;P&gt;Why this works this way is because each BindingSource object manages it's own position within the data source that is bound to it. If you use one LookupBindingSource in this example for multiple combos, then as one selection is made, the other will also change. (Keep in mind that using multiple LookupBindingSources&amp;nbsp;&lt;STRONG&gt;does not &lt;/STRONG&gt;load multiple instances of the LookupTable's data from the database, it just simply creates multiple BindingSource objects.)&lt;/P&gt;
&lt;P&gt;So the customer easily fixed this issue and was very happy with the &lt;A class="" href="http://blogs.msdn.com/vbteam/" target=_blank mce_href="http://blogs.msdn.com/vbteam/"&gt;VB Team's&lt;/A&gt; quick response. &lt;/P&gt;
&lt;P&gt;Enjoy!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=5001362" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2005/default.aspx">VS2005</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Community/default.aspx">Community</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/DevCenter/default.aspx">DevCenter</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category></item><item><title>Resizing Images Stored in SQL-Server</title><link>http://blogs.msdn.com/bethmassi/archive/2007/08/28/rezising-images-stored-in-sql-server.aspx</link><pubDate>Tue, 28 Aug 2007 21:35:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:4616925</guid><dc:creator>Beth Massi</dc:creator><slash:comments>23</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/4616925.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=4616925</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=4616925</wfw:comment><description>&lt;P&gt;In SQL-Server we can store images inside database tables directly&amp;nbsp;using the Image column type. And with .NET 2.0 data binding it can automatically convert these images (stored as byte arrays) into System.Drawing.Image classes for you. For instance,&amp;nbsp;say you have a table called Pictures that has a column called Picture of the data type Image. You can create a new data source to this table to generate a strongly typed DataSet and use drag-and drop data binding to automatically display the image in a PictureBox. (For information on connecting to your database and creating strongly typed DataSets &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb643826.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb643826.aspx"&gt;watch this video&lt;/A&gt;.) &lt;/P&gt;
&lt;P&gt;If you don't see a PictureBox icon in the Data Sources window next to the Picture column in your DataTable, you may need to select the PictureBox to associate the byte array with the control:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;IMG style="WIDTH: 228px; HEIGHT: 306px" height=306 src="http://blogs.msdn.com/photos/bethmassi/images/4616617/original.aspx" width=228 mce_src="http://blogs.msdn.com/photos/bethmassi/images/4616617/original.aspx"&gt;&lt;/P&gt;
&lt;P&gt;Now you can drag-and-drop the Picture onto your form to set up the data binding to the PictureBox. The&amp;nbsp;binding will automatically handle converting the byte array stored in the column into an image using the System.Drawing.ImageConverter. You can then set the SizeMode property on the PictureBox depending on how you want the image displayed; resized, stretched or otherwise. &lt;/P&gt;
&lt;P&gt;But what if you just want to resize the image and not use a PictureBox? In that case you just need to convert the byte array stored in the Picture column into an System.Drawing.Image. Once you have that then you can use methods on this Image class to perform all sorts of transformations. To resize the image,&amp;nbsp;just call GetThumbnailImage and pass it the new size requirements:&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; resizeImg, origImg &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; Image&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;'Get the current row&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; row &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; PictureDemoDataSet.PictureRow&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;row = &lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.PictureBindingSource.Current, DataRowView).Row, _&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PictureDemoDataSet.PictureRow)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;'Convert byte array to image&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Using&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; ms &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; System.IO.MemoryStream(row.Picture)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;origImg = Image.FromStream(ms)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; width &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt; = 25&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; height &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt; = 25&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;'Resize image&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;resizeImg = origImg.GetThumbnailImage(width, height, &lt;SPAN style="COLOR: blue"&gt;Nothing&lt;/SPAN&gt;, &lt;SPAN style="COLOR: blue"&gt;Nothing&lt;/SPAN&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Using&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;A class="" href="http://blogs.msdn.com/bethmassi/attachment/4616925.ashx" mce_href="http://blogs.msdn.com/bethmassi/attachment/4616925.ashx"&gt;I've attached a simple application&lt;/A&gt; that demonstrates these techniques for you to play with. You'll need Visual Basic 2005 and SQL Server 2005 (or Express) to run.&lt;/P&gt;
&lt;P&gt;Enjoy!&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=4616925" width="1" height="1"&gt;</description><enclosure url="http://blogs.msdn.com/bethmassi/attachment/4616925.ashx" length="3485751" type="application/x-zip-compressed" /><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Building a Secure Login Form (Parameterized Queries Part 2)</title><link>http://blogs.msdn.com/bethmassi/archive/2007/06/06/login-form-parameterized-queries-part-2.aspx</link><pubDate>Thu, 07 Jun 2007 08:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2993852</guid><dc:creator>Beth Massi</dc:creator><slash:comments>27</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/2993852.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=2993852</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=2993852</wfw:comment><description>&lt;P&gt;&lt;A class="" href="http://blogs.msdn.com/bethmassi/archive/2007/05/25/creating-a-parameterized-query.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/05/25/creating-a-parameterized-query.aspx"&gt;In my first post on&amp;nbsp;parameterized queries&lt;/A&gt;&amp;nbsp;I built a simple login form that really was a contrived example meant to showcase how to use the &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/dex7k4dw(VS.80).aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/dex7k4dw(VS.80).aspx"&gt;TableAdapter Configuration Wizard&lt;/A&gt; to configure a parameterized query. However, since I opened myself up here, I felt it socially responsible to show how to make this a bit more secure by showing you how to store passwords in a secure way in a database. &lt;/P&gt;
&lt;P&gt;There are actually many many secure ways to store data in your database including using the &lt;A class="" href="http://aspnet.4guysfromrolla.com/articles/021407-1.aspx" target=_blank mce_href="http://aspnet.4guysfromrolla.com/articles/021407-1.aspx"&gt;encryption features of SQL-Server 2005&lt;/A&gt;&amp;nbsp;which allows you to protect columns inside your database at the database level, independent of the application. Additionally, if we're just talking about user's logins and you're building a multi-tier or SO application then using the ASP.NET membership services is probably your best choice.&lt;A class="" href="http://blogs.msdn.com/brada/archive/2007/05/23/net-client-application-services.aspx" target=_blank mce_href="http://blogs.msdn.com/brada/archive/2007/05/23/net-client-application-services.aspx"&gt; In next version of Visual Studio, Microsoft made these ASP.NET services easily&amp;nbsp;accessible to any client application&lt;/A&gt; (Winforms, WPF, Silverlight) not just WebForms. But what if you're building a single-user application or a client-server app with only a handful of users and you don't have (or want) a web server or maybe you're not using SQL-Server as your database? &lt;/P&gt;
&lt;P&gt;The first recommendation for this scenario is don't store passwords at all. If you don't have passwords in your application then you don't have to worry about someone stealing them. Instead, consider using the Windows Identity as the user of your application. This means that your application will not need to store passwords, only user names, because it would be using the logged in Windows user which has already been authenticated through the Windows OS. To access the user name of the currently logged in user from your client code:&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; System.Security.Principal&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;...&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; user &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; WindowsIdentity = WindowsIdentity.GetCurrent()&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; userName &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;&lt;FONT color=#000000&gt; = user.Name&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;/SPAN&gt;
&lt;P&gt;The only thing you would need to do in your database Login table is make the UserName field unique. Then you could write a very simple parameterized query. &lt;/P&gt;
&lt;P&gt;SELECT COUNT(*) FROM Login WHERE UserName = @UserName&lt;/P&gt;
&lt;P&gt;So your login code would be very simple. If you name the above parameterized query on your TableAdapter "GetLoginByUserName" then it would be something like:&lt;/P&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; System.Security.Principal&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;...&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; user &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; WindowsIdentity = WindowsIdentity.GetCurrent()&lt;BR&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; userName &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;&lt;FONT color=#000000&gt; = user.Name&lt;/FONT&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;If&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;&lt;FONT color=#000000&gt;(&lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;&lt;FONT color=#000000&gt;.LoginTableAdapter1.GetLoginByUserName(userName), &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt;&lt;FONT color=#000000&gt;) = 1 &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;Then&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;MsgBox(&lt;/FONT&gt;&lt;SPAN style="COLOR: #a31515"&gt;"Welcome to my application!"&lt;/SPAN&gt;&lt;FONT color=#000000&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Else&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;MsgBox(&lt;/FONT&gt;&lt;SPAN style="COLOR: #a31515"&gt;"Invalid username or password."&lt;/SPAN&gt;&lt;FONT color=#000000&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;FONT color=#000000&gt; &lt;/FONT&gt;&lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;/SPAN&gt;(NOTE: This code assumes that the application is connecting to the database directly and not through a service layer. If you are connecting to a service layer then you need to configure your web server to authenticate Windows users by not allowing anonymous access and only allowing Windows Authentication. &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/aa480475.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa480475.aspx"&gt;For more information, read this&lt;/A&gt;.)&lt;/P&gt;
&lt;P&gt;However, what if you cannot use this method of authentication?&amp;nbsp;For instance,&amp;nbsp;your application runs on a shared&amp;nbsp;computer that&amp;nbsp;remains logged in under one Windows&amp;nbsp;login, but you require users to login separately to your application. In that case you're going to need to store passwords. However, if we store passwords as clear text in our database, anybody that can get a glimpse of the&amp;nbsp;Login table will have a bunch of user credentials to access the application! The safest thing to do is to use a one-way hashing algorithm and store the hashes in your database table instead. The .NET Framework gives you a lot of help here by providing a handful of proven hashing algorithms in the &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.aspx"&gt;System.Security.Cryptography&lt;/A&gt; namespace. The most common are &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.sha1.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.sha1.aspx"&gt;SHA-1&lt;/A&gt; and &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.md5.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.md5.aspx"&gt;MD5&lt;/A&gt;. To hash a string using the SHA-1 becomes very simple in .NET:&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;FONT face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; System.Security.Cryptography&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; System.Text&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;...&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="COLOR: blue"&gt;Function&lt;/SPAN&gt; HashEncryptString(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; s &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;) &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; hasher &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; SHA1CryptoServiceProvider()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; clearBytes &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;() = Encoding.UTF8.GetBytes(s)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; hashedBytes &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;() = hasher.ComputeHash(clearBytes)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Return&lt;/SPAN&gt; Convert.ToBase64String(hashedBytes)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;Hash algorithms are one-way so it's very very difficult to tell what the original password is from a computed hash. (So if a user forgets their password, you won't be able to tell them what it was.) So when we store user names and passwords in our Login table we can easily hash the value of the submitted password and store that instead. So we're secure now, right? Well almost! Let's take a look at my Login table in this example:&lt;/P&gt;
&lt;P mce_keep="true"&gt;&lt;IMG style="WIDTH: 471px; HEIGHT: 152px" height=152 src="http://blogs.msdn.com/photos/bethmassi/images/3132451/original.aspx" width=471 mce_src="http://blogs.msdn.com/photos/bethmassi/images/3132451/original.aspx"&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;Notice that Beth and Joe both have the &lt;EM&gt;same hashed password&lt;/EM&gt;. This means that both these passwords are the same as clear text as well. An attacker could probably figure out the password by using a &lt;A class="" href="http://en.wikipedia.org/wiki/Dictionary_attack" target=_blank mce_href="http://en.wikipedia.org/wiki/Dictionary_attack"&gt;dictionary attack&lt;/A&gt; on our table. So what can we do?&lt;/P&gt;
&lt;P mce_keep="true"&gt;There's a technique called &lt;EM&gt;salting &lt;/EM&gt;where you take the password and "salt" it with a random value and then hash that. This random value is different for each login. This will create different hashed passwords for the same clear text password, making it extremely difficult to break. To be even more secure, you should store this salt value in a separate table from the passwords. To obtain an appropriate&amp;nbsp;salt (random) value&amp;nbsp;in .NET you can use the &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider.aspx"&gt;RNGCryptoServiceProvider&lt;/A&gt;&amp;nbsp;class. &lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; System.Security.Cryptography&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;...&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Function&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; GetSalt(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; saltSize &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt;) &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; buffer() &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt; = &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;(saltSize) {}&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; rng &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; RNGCryptoServiceProvider()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;rng.GetBytes(buffer)&lt;BR&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Return&lt;/SPAN&gt; Convert.ToBase64String(buffer)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;
&lt;P mce_keep="true"&gt;So now we can take the salt value and store that in a table called Salt which has a foreign key to our Login table. Then I can create a couple parameterized queries on my TableAdapters for Login and Salt.&amp;nbsp; &lt;/P&gt;
&lt;P mce_keep="true"&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/3132890/340x375.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/3132890/340x375.aspx"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;On the LoginTableAdapter we can add a parameterized query called GetLoginByUserNameAndPassword where we pass the UserName and the salted hashed password. The select statement returns a scalar value and we add it through the TableAdapter Query Configuration Wizard just like I &lt;A class="" href="http://blogs.msdn.com/bethmassi/archive/2007/05/25/creating-a-parameterized-query.aspx" target=_blank mce_href="http://blogs.msdn.com/bethmassi/archive/2007/05/25/creating-a-parameterized-query.aspx"&gt;showed in my previous post&lt;/A&gt;.&lt;/P&gt;
&lt;P mce_keep="true"&gt;SELECT COUNT(*) FROM Login WHERE UserName = @UserName AND Password = @Password&lt;/P&gt;
&lt;P mce_keep="true"&gt;In order to pass the correct value for the @Password parameter, we need the salt value first. On the SaltTableAdapter we can add a parameterized query that returns the salt value for a given UserName called GetSaltByUserName.&lt;/P&gt;
&lt;P mce_keep="true"&gt;SELECT TOP (1) Salt.Salt FROM&amp;nbsp;Salt INNER JOIN&amp;nbsp;Login ON Salt.LoginID = Login.LoginID WHERE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (Login.UserName = @UserName)&lt;/P&gt;
&lt;P mce_keep="true"&gt;To make it easier to access the hashing functions we can create a module called PasswordCrypto:&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; System.Security.Cryptography&lt;BR&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Imports&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;/SPAN&gt;System.Text&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Module&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; PasswordCrypto&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Private&lt;/SPAN&gt; Hasher &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; SHA1CryptoServiceProvider()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Friend&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;/SPAN&gt; GetSalt(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; saltSize &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt;) &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; buffer() &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt; = &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;(saltSize) {}&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; rng &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; RNGCryptoServiceProvider()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;rng.GetBytes(buffer)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Return&lt;/SPAN&gt; Convert.ToBase64String(buffer)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Friend&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;/SPAN&gt; HashEncryptString(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; s &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;) &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; clearBytes &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;() = Encoding.UTF8.GetBytes(s)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; hashedBytes &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Byte&lt;/SPAN&gt;() = Hasher.ComputeHash(clearBytes)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Return&lt;/SPAN&gt; Convert.ToBase64String(hashedBytes)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Friend&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;/SPAN&gt; HashEncryptStringWithSalt(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; s &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;, _&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; salt &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;) &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;BR&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Return&lt;/SPAN&gt; HashEncryptString(salt + s)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Function&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Module&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;
&lt;P mce_keep="true"&gt;Now that we have our hashing code and our TableAdapters configured, taking our Login form we can add code like this to verify whether a user's entered password matches the hashed password in the Login table:&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Try&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; isOK &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Boolean&lt;/SPAN&gt; = &lt;SPAN style="COLOR: blue"&gt;False&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;'Get the salt value for this username&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; saltValue &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Object&lt;/SPAN&gt; = _&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.SaltTableAdapter1.GetSaltByUserName(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.txtUserName.Text)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Not&lt;/SPAN&gt; IsDBNull(saltValue) &lt;SPAN style="COLOR: blue"&gt;Then&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;'Hash the user entered password with the salt value stored in the Salt table&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;Dim&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; password &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt; = _&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PasswordCrypto.HashEncryptStringWithSalt(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.txtPassword.Text, saltValue.ToString)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: green; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;'Now check the Login table to see if this hashed password matches&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;isOK =&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.LoginTableAdapter1.GetLoginByUserNameAndPassword( _&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.txtUserName.Text, password), &lt;SPAN style="COLOR: blue"&gt;Integer&lt;/SPAN&gt;) = 1&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;If&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;If&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; isOK &lt;SPAN style="COLOR: blue"&gt;Then&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;MsgBox(&lt;SPAN style="COLOR: #a31515"&gt;"Welcome to my Application!"&lt;/SPAN&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;Else&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;MsgBox(&lt;SPAN style="COLOR: #a31515"&gt;"Invalid user name or password."&lt;/SPAN&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;If&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Catch&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; ex &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; Exception&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp; &lt;/SPAN&gt;MsgBox(ex.ToString)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Try&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;So this is how we can store passwords in a secure way in our database, even if our database does not support encrypted columns. With .NET, accessing hashing algorithms is a snap. &lt;A class="" href="http://blogs.msdn.com/bethmassi/attachment/2993852.ashx" mce_href="http://blogs.msdn.com/bethmassi/attachment/2993852.ashx"&gt;I've attached a complete sample&lt;/A&gt; that demonstrates these techniques (as well as saving users passwords and salts) so that you can learn from them. You'll need Visual Studio or &lt;A class="" href="http://msdn.microsoft.com/vstudio/express/vb/" target=_blank mce_href="http://msdn.microsoft.com/vstudio/express/vb/"&gt;Visual Basic Express&lt;/A&gt; and &lt;A class="" href="http://msdn.microsoft.com/vstudio/express/sql/" target=_blank mce_href="http://msdn.microsoft.com/vstudio/express/sql/"&gt;SQL-Server Express&lt;/A&gt; installed to compile and run the sample.&lt;/P&gt;
&lt;P mce_keep="true"&gt;Enjoy! And be secure!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2993852" width="1" height="1"&gt;</description><enclosure url="http://blogs.msdn.com/bethmassi/attachment/2993852.ashx" length="179027" type="application/x-zip-compressed" /><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2005/default.aspx">VS2005</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item><item><title>Many-to-Many Data Binding</title><link>http://blogs.msdn.com/bethmassi/archive/2007/05/30/many-to-many-data-binding.aspx</link><pubDate>Wed, 30 May 2007 21:25:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2992513</guid><dc:creator>Beth Massi</dc:creator><slash:comments>27</slash:comments><comments>http://blogs.msdn.com/bethmassi/comments/2992513.aspx</comments><wfw:commentRss>http://blogs.msdn.com/bethmassi/commentrss.aspx?PostID=2992513</wfw:commentRss><wfw:comment>http://blogs.msdn.com/bethmassi/rsscomments.aspx?PostID=2992513</wfw:comment><description>&lt;P&gt;In my &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx"&gt;Forms over Data videos series&lt;/A&gt; I show you how to create a one-to-many data entry form in video #3. Recently, I've had a few people ask how to create a form that displays a many-to-many relationship so I thought I'd post on how to do that today. &lt;/P&gt;
&lt;P&gt;You can actually think of a many-to-many relationship as two one-to-many's and depending on how you are designing your UI, you may be displaying it one way or the other. Take the many-to-many relationship Orders&amp;nbsp;--&amp;lt; OrderDetail &amp;gt;-- Product where an Order has many OrderDetails and Product also has many OrderDetails. Typically when users are entering the data, we're choosing to display one of the one-to-many relationships for editing. For instance we could have users enter one order at a time that displayed the Order fields as textboxes and the OrderDetails in a grid. Then we could choose Products from a dropdown list in the OrderDetails grid. (The Products are edited elsewhere on the Product Catalog form for instance.)&lt;/P&gt;
&lt;P&gt;However when we want to display or report on data, we don't necessarily need it to be easy to enter the data, we want to be able to easily &lt;EM&gt;see &lt;/EM&gt;the data instead. And depending on your UI and your target users, you still may be able to easily guide the users through editing. But what if we wanted to just get a list of all the Products on a particular Order? Or know all of the Orders for a particular Product? These are easy SQL queries but what if we want to reuse a&amp;nbsp;DataSet we already have built, or we want to allow editing of the data?&lt;/P&gt;
&lt;P&gt;Say I have a DataSet of what we're describing now: Orders&amp;nbsp;--&amp;lt; OrderDetail &amp;gt;-- Product.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/2991927/440x375.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/2991927/440x375.aspx"&gt;&lt;/P&gt;
&lt;P&gt;We want a form that will allow the users to scroll through a list of orders and see all the products on that order. So we just want two grids on a form, one of Orders and one of Product. To get the filtering of rows set up properly, we will use the same exact technique for setting up automatic filtering on a One-to-Many form, but with one additional manual step which I'll show you isn't so bad.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;&lt;IMG style="WIDTH: 245px; HEIGHT: 209px" height=209 src="http://blogs.msdn.com/photos/bethmassi/images/2992141/original.aspx" width=245 align=left mce_src="http://blogs.msdn.com/photos/bethmassi/images/2992141/original.aspx"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;To get the form quickly designed and set up, from the Data Sources window&amp;nbsp;I just drag the Orders table and it's related OrderDetails onto the Form. Make sure you select the related OrderDetail table under the Order table otherwise you won't get the automatic filtering set up on the BindingSources it creates for you.&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;Then drag the Product table onto the form and&amp;nbsp;&lt;EM&gt;delete&lt;/EM&gt; the OrderDetails grid becasue we don't want to display that to the user. This process sets up all the binding components in the Form's component tray properly for all three of our DataTables.&amp;nbsp;It should look something like this:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://blogs.msdn.com/photos/bethmassi/images/2992051/500x374.aspx" mce_src="http://blogs.msdn.com/photos/bethmassi/images/2992051/500x374.aspx"&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;Now when the position changes in the OrdersBindingSource, the OrderDetailBindingSource will filter the proper rows based on the selected Order. All that's left to do is to filter the ProductBindingSource based on this list of OrderDetails. So open up the code behind and we're going to handle the OrderDetailBindingSource.ListChanged event. In this handler we'll create the filter to apply on the ProductBindingSource based on the filtered rows in the OrderDetailsBindingSource.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Private&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt; OrderDetailBindingSource_ListChanged(&lt;SPAN style="COLOR: blue"&gt;ByVal&lt;/SPAN&gt; sender &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Object&lt;/SPAN&gt;, _&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;ByVal&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; e &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; System.ComponentModel.ListChangedEventArgs) _&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;Handles&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; OrderDetailBindingSource.ListChanged&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.OrderDetailBindingSource.Count &amp;gt; 0 &lt;SPAN style="COLOR: blue"&gt;Then&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Dim&lt;/SPAN&gt; filter &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;New&lt;/SPAN&gt; System.Text.StringBuilder()&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;For&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;Each&lt;/SPAN&gt; orderDetail &lt;SPAN style="COLOR: blue"&gt;As&lt;/SPAN&gt; DataRowView &lt;SPAN style="COLOR: blue"&gt;In&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;CType&lt;/SPAN&gt;(&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.OrderDetailBindingSource.List, DataView)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;If&lt;/SPAN&gt; filter.Length &amp;lt;&amp;gt; 0 &lt;SPAN style="COLOR: blue"&gt;Then&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;' Adding criteria&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;filter.Append(&lt;SPAN style="COLOR: #a31515"&gt;" OR "&lt;/SPAN&gt;)&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;If&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;filter.Append(&lt;SPAN style="COLOR: blue"&gt;String&lt;/SPAN&gt;.Format(&lt;SPAN style="COLOR: #a31515"&gt;"ProductID = {0}"&lt;/SPAN&gt;, orderDetail!ProductID))&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Next&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductBindingSource.Filter = filter.ToString&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Else&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: green"&gt;' display no rows&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;Me&lt;/SPAN&gt;.ProductBindingSource.Filter = &lt;SPAN style="COLOR: #a31515"&gt;"1 = 0"&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal; mso-layout-grid-align: none"&gt;&lt;SPAN style="FONT-SIZE: 8pt; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;&lt;SPAN style="mso-spacerun: yes"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;End&lt;/SPAN&gt; &lt;SPAN style="COLOR: blue"&gt;If&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN: 0in 0in 10pt"&gt;&lt;SPAN style="FONT-SIZE: 8pt; COLOR: blue; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt;End&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%; FONT-FAMILY: 'Courier New'; mso-no-proof: yes"&gt; &lt;SPAN style="COLOR: blue"&gt;Sub&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN style="FONT-SIZE: 8pt; LINE-HEIGHT: 115%"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P mce_keep="true"&gt;
&lt;P mce_keep="true"&gt;Now when we run this form, as the user scrolls through the Order rows in the first grid, the Product rows are displayed in the second grid for the entire order. To really understand what is going on just take a look at how the BindingSources are set up in the property sheet. The OrdersBindingSource has the DataSet as the Datasource and the DataMember is set to "Order". Then the OrderDetailBindingSource has&amp;nbsp;its Datasource set to the OrdersBindingSource and its DataMember to "OrderDetails". This sets up the One-to-Many chaining and automatic filtering as explained in video #3 of my &lt;A class="" href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx"&gt;Forms over Data videos series&lt;/A&gt;. &lt;/P&gt;
&lt;P mce_keep="true"&gt;All that's left in this case is getting the filtering on the ProductBindingSource which has the same Datasource as the OrderBindingsource, the DataSet,&amp;nbsp;but its DataMember is set to "Product". We do this by simply handling the ListChanged event on the OrderDetailBindingSource. These grids are editable too, just be careful that your users understand the filtering that's being applied when working with the Product grid. &lt;/P&gt;
&lt;P mce_keep="true"&gt;Happy Data Binding!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2992513" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Visual+Basic/default.aspx">Visual Basic</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/VS2005/default.aspx">VS2005</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Winforms/default.aspx">Winforms</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Article/default.aspx">Article</category><category domain="http://blogs.msdn.com/bethmassi/archive/tags/Data/default.aspx">Data</category></item></channel></rss>