<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>.NET (C# / VB)</title>
        <link>http://weblogs.sqlteam.com/jeffs/category/158.aspx</link>
        <description>.NET is great, and I cover it a lot in my blog posts.  SQL Server and .NET make such a great team that it can be tough to discuss one without the other.</description>
        <language>en-US</language>
        <copyright>Jeff Smith</copyright>
        <managingEditor>smith_jeffreyt@yahoo.com</managingEditor>
        <generator>Subtext Version 1.9.4.0</generator>
        <item>
            <title>Minimize a DropDownList's ViewState</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx</link>
            <description>Let's say you have a very large DropDownList with lots of values and text.  We need to maintain ViewState in this DropDownList so that we can retrieve the selected value on a post back.   Of course, this means that now the ViewState contains the data for every single value in the list, both values and text included.   Even though the page itself may be fairly simple and lightweight, the result of having this simple DropDownList on the page is that the page size is quite large and the amount of data passed back and forth on a postback is very large as well.&lt;br /&gt;
&lt;br /&gt;
If you have an efficient database, and/or if you are caching the data anyway, you might not mind re-loading the list items each time the page posts back to eliminate the need for the page itself to hold all of this ViewState data.  &lt;br /&gt;
&lt;br /&gt;
However, if you turn off ViewState on the DropDownList, you will notice that it now does not remember the selected value on post backs.  The solution is very simple -- just manually set the DropDownList after re-loading it to the value from the HTML form post.&lt;br /&gt;
&lt;br /&gt;
To do this, first we disable ViewState on the DropDownList (EnableViewState="False").  Then, instead of a typical PageLoad() method like this:
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;protected&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Page_Load(&lt;span style="color: blue;"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43, 145, 175);"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!IsPostBack)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;               &lt;span style="font-weight: bold;"&gt; loadList();   &lt;/span&gt;            &lt;br /&gt;
&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: green;"&gt;// other stuff here&lt;/span&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            }&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
we would load the list every time the page posts back:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;protected&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Page_Load(&lt;span style="color: blue;"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43, 145, 175);"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="font-weight: bold;"&gt;loadList();    &lt;/span&gt;  &lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!IsPostBack)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                 &lt;span style="color: green;"&gt;// other stuff here&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            }&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
And, in that loadList() method, instead of simply doing this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;void&lt;/span&gt; loadList()&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataSource = &lt;span style="color: green;"&gt;// get data from DB&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataBind();&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
you also would check to see if it has a posted value:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;void&lt;/span&gt; loadList()&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataSource = &lt;span style="color: green;"&gt;// get data from DB&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataBind();&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;          &lt;span style="font-weight: bold;"&gt;  &lt;/span&gt;&lt;span style="color: blue; font-weight: bold;"&gt;string&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; r = Request[dlTest.UniqueID];&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px; font-weight: bold;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (r != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/p&gt;
&lt;p style="margin: 0px; font-weight: bold;"&gt;                dlTest.SelectedValue = r;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;
That loads the DropDownList each time and ensures that the value set is what was posted back.  Thus, the control will now "remember" the selected value on each postback, but without requiring any data stored in the ViewState at all.  &lt;br /&gt;
&lt;br /&gt;
It would also be easy to implement this logic in a control that inherits from DropDownList; say, a LightDropDownList.  Techniques like this will of course work on other controls as well, such as a ListBox or a CheckedList.&lt;br /&gt;
&lt;br /&gt;
With fast database connections and performance, as well as data caching, sometimes persisting data on the page is not the optimal way to go.  Like always, there are different ways to skin a cat and sometimes simple little tweaks like this can have huge benefits on performance.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Update:  &lt;/span&gt;See the comments for other options are that even easier/better.  Also in the comments, Richard has provided a link to this &lt;a target="_blank" href="http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx"&gt;excellent post on ViewState&lt;/a&gt; that I highly recommend checking out.  It's long, but very informative and quite entertaining as well.   Thanks for the great feedback.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60569.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx</guid>
            <pubDate>Thu, 17 Apr 2008 16:21:16 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60569.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60569.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60569.aspx</trackback:ping>
        </item>
        <item>
            <title>DataTable, DataView and CreateDataReader</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/02/22/60528.aspx</link>
            <description>Here's something I was not aware of in .NET 2.0+ that I recently discovered.&lt;br /&gt;
&lt;br /&gt;
There is a new &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.datatablereader.aspx" target="_blank"&gt;DataTableReader&lt;/a&gt; class that implements &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.idatareader.aspx" target="_blank"&gt;IDataReader&lt;/a&gt; so that you can loop through an in-memory &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.datatable.aspx" target="_blank"&gt;DataTable&lt;/a&gt; using the same interface that you would to loop through a &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx" target="_blank"&gt;SqlDataReader&lt;/a&gt;.  You can create this object by using the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.datatable.createdatareader.aspx" target="_blank"&gt;CreateDataReader&lt;/a&gt;() method of a DataTable.  This is great because now you can create a generic ReadFromDatabase() method for your classes that accepts an IDataReader parameter and you can create those objects from pretty much any data source, including DataTables, which wasn't as easy to do previously.&lt;br /&gt;
&lt;br /&gt;
There is one catch: There is no CreateDataReader() method for the &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.dataview.aspx" target="_blank"&gt;DataView&lt;/a&gt; class, meaning if you want to use a DataTableReader to loop through a DataTable that is sorted or filtered, you are out of luck.  This was pretty disappointing -- in fact, I would think that CreateDataReader(), if only implemented in one place, makes more sense to implement in the DataView class rather than the DataTable class, since you would then get the best of both worlds.&lt;br /&gt;
&lt;br /&gt;
The way to solve this is that you unfortunately need create another DataTable that contains the data loaded from your sorted and/or filtered DataView.  Luckily, there is a ToTable() method in the DataView class that allows you to do this.&lt;br /&gt;
&lt;br /&gt;
Here's an example of creating a DataTableReader that enumerates a DataView:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px; font-family: Courier New;"&gt;DataTable t = ... some data table here ...&lt;br /&gt;
t.DefaultView.Sort = ... your sort here ...&lt;br /&gt;
t.DefaultView.RowFilter = ... your filter here ...&lt;br /&gt;
&lt;br /&gt;
DataTableReader r = t.DefaultView.ToTable().CreateDataReader();&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Not ideal, but it works.  The benefit of being able to use clean, generic code that accepts IDataReader objects and therefore allows you to use very efficient SqlDataReaders whenever possible makes it worth it.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Update: &lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;I have created a simple &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2008/02/28/DataViewReader.aspx"&gt;DataViewReader class here&lt;/a&gt; if you need this functionality. Using this will be much more efficient than creating a brand new DataTable if you would like to create an IDataReader on a DataView.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60528.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/02/22/60528.aspx</guid>
            <pubDate>Fri, 22 Feb 2008 17:30:19 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60528.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/02/22/60528.aspx#feedback</comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60528.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60528.aspx</trackback:ping>
        </item>
        <item>
            <title>Splitting a single DataTable into Parent/Child DataTables for Hierarchical Processing (e.g., nested ASP.NET Repeater controls)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx</link>
            <description>In ASP.NET, we often would like to output "grouped" data on our web pages, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Customer Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;-------- ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;ABC Foundation&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;The XLZ Company&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
   &lt;br /&gt;
The easiest way to do this is with nested &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.repeater(VS.80).aspx" target="_blank"&gt;Repeater&lt;/a&gt; controls; one for the outer group (Customers, this case), and within that Repeater's ItemTemplate we'd have another Repeater control for the details (Products).  &lt;br /&gt;
&lt;br /&gt;
To use nested repeaters, you would return two separate result sets from SQL:  &lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;A "Customers" result set, listing one row per Customer to display on the report&lt;/li&gt;
    &lt;li&gt;A "Products" result set, listing one row per Product to display.&lt;/li&gt;
&lt;/ol&gt;
You would put both of those results into a single DataSet as DataTables, and then create a relation between the two (on the CustomerID column).   Now, in our DataSet, we have a parent-child relation and each row in the parent Customers DataTable will have related child rows in the Products DataTable.&lt;br /&gt;
&lt;br /&gt;
Then, in ASP.NET, we create nested repeaters:  the outer repeater is bound to the Customers DataTable, and the inner repeater gets bound for each Customer row to the child rows in the Products DataTable.    There are lots of good examples detailing this technique &lt;a target="_blank" href="http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185"&gt;here&lt;/a&gt; and &lt;a target="_blank" href="http://msdn2.microsoft.com/en-us/library/aa478959.aspx"&gt;here&lt;/a&gt;, so I won't get into specifics on that in this space.&lt;br /&gt;
&lt;br /&gt;
However -- what if you already have a stored procedure that returns this data in a single result set? The nested Repeater controls won't do you much good, since we need two separate (but related) DataTables.  Or, what if it is inefficient to return these results separately in SQL?  For example, suppose we want to output only the products for customers that have a total sales of greater than $200 for a specified time period.  We'd need to process the sales transactions table &lt;span style="font-style: italic;"&gt;twice&lt;/span&gt; in SQL to generate the list of Customers separate from the list of Products.  Thus, our SQL becomes twice as inefficient as it needs to be to use this technique.&lt;br /&gt;
&lt;br /&gt;
To accommodate those possibilities, I recently wrote a simple function in C# that takes a single DataTable and creates a related parent DataTable in the same DataSet based on specified parameters.  This is useful if you only have a single result set from SQL (or any other data source) and you'd like to break that data up into two separate parent-child DataTables in order to use nested repeaters or any other type of parent-child processing on the data.&lt;br /&gt;
&lt;br /&gt;
As an example, if you start with a single DataTable like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Products:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  CustomerName    Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  --------------- ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
We can pass that DataTable to the CreateParentTable() function and you would end up with two related DataTables in the DataSet, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Customers:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  CustomerName    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  --------------- &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Products:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Then we can simply bind the outer Repeater to the Customers DataTable, and then bind the inner Repeater to a call to each DataRow's GetChildRows() method to get the related products.  All from a single SQL result set.&lt;br /&gt;
&lt;br /&gt;
Also, notice that the function removes the CustomerName column from the Products DataTable, but leaves the CustomerID column for the relation.  &lt;br /&gt;
&lt;br /&gt;
Here's the function:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   Creates a parent DataTable within the DataSet using distinct rows from&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   an existing "source" DataTable, based on the column(s) specified.&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   The source table then becomes the "child" table of the newly created &lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   parent. A DataRelation is also created between the parent table and the &lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   child table, using the column(s) specified.&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="sourceTable"&amp;gt;&lt;br /&gt;
///    The source DataTable, which must be within a DataSet.   &lt;br /&gt;
///    This source table will become the "child" table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="parentTableName"&amp;gt;&lt;br /&gt;
///    This name will be assigned to the parent table once it is created.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="relationColumns"&amp;gt;&lt;br /&gt;
///    Specify the columns used to relate the parent table to the child table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="additionalColumns"&amp;gt;&lt;br /&gt;
///    Any additional column(s) in the source table that will be extracted to the &lt;br /&gt;
///    parent table.  These columns will be removed from the source table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="relationName"&amp;gt;&lt;br /&gt;
///    The name of the relation that will be created between the parent and &lt;br /&gt;
///    the child table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;span style="color: rgb(51, 153, 102);"&gt;/// &lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;public static void AddParentTable(DataTable sourceTable, string parentTableName, &lt;br /&gt;
     string[] relationColumns, string[] additionalColumns, string relationName)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;{&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataSet dataSet = sourceTable.DataSet;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    if (dataSet == null)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        throw new Exception("The source DataTable must be contained in a DataSet");&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // generate the set of columns to use to create the Parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    string[] cols = new string[relationColumns.Length+additionalColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    relationColumns.CopyTo(cols,0);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    additionalColumns.CopyTo(cols,relationColumns.Length);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // create the parent table, copying unique rows from the Child table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataTable parent = sourceTable.DefaultView.ToTable(parentTableName, true, cols);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // add the parent table to the DataSet:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    dataSet.Tables.Add(parent);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // remove the additional columns from the child table that were &lt;br /&gt;
    // copied to the parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    foreach (string s in additionalColumns)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        sourceTable.Columns.Remove(s);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // create the relation between the new parent table and the child table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataColumn[] parentColumns = new DataColumn[relationColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataColumn[] childColumns = new DataColumn[relationColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    for (int i=0; i&amp;lt; relationColumns.Length;i++)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        parentColumns[i] = parent.Columns[relationColumns[i]];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        childColumns[i] = sourceTable.Columns[relationColumns[i]];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    }&lt;br /&gt;
&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // And, finally, add the relation to the parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    parent.ChildRelations.Add(relationName, parentColumns, childColumns);&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The parameters are all explained in the XML documentation.  Feel free to make modifications or overloads of this function.  Some good overloads might be one that accepts a single relation column name, or that accepts a parameter that determines whether or not columns are removed from the child table.&lt;br /&gt;
    &lt;br /&gt;
Using our example, we'd call it like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;SqlConnection cn = new SqlConnection(&lt;span style="font-style: italic;"&gt;&amp;lt;... your connection string ...&amp;gt;&lt;/span&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;SqlCommand cm = new SqlCommand(&lt;span style="font-style: italic;"&gt;&amp;lt;... the name of the stored proc ...&amp;gt;&lt;/span&gt;, cn);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cm.CommandType= CommandType.StoredProcedure;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cn.Open();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;SqlDataAdapter da = new SqlDataAdapter(cm);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;DataSet ds = new DataSet();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;da.Fill(ds, "Products");&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CreateParentTable(ds.Tables["Products"], "Customers", new string[] {"CustomerID"}, &lt;br /&gt;
    new string[] {"CustomerName"}, "CustomerProducts")&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
After that is executed, the DataSet now contains an additional "Customers" table, and a relation called "CustomerProducts" to the "Products" table.   The relation is on "CustomerID", and the "Customers" table also includes the "CustomerName" column.  &lt;br /&gt;
&lt;br /&gt;
Thus, if we had nested repeaters in our ASP.NET code like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt; &amp;lt;table&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &amp;lt;th&amp;gt;Customer&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;th&amp;gt;Product&amp;lt;/th&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;th&amp;gt;Sales&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;asp:Repeater runat="server" ID="rpt1"&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;td colspan="3"&amp;gt;&amp;lt;%# ((System.Data.DataRowView) Container.DataItem)["CustomerName"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;/tr&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;asp:Repeater runat="server" ID="rpt2"  &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;          DataSource='&amp;lt;%# ((System.Data.DataRowView) Container.DataItem).Row.GetChildRows("CustomerProducts") %&amp;gt;'&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt; &amp;lt;%# ((System.Data.DataRow) Container.DataItem)["Product"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt; &amp;lt;%# ((System.Data.DataRow) Container.DataItem)["Sales"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &amp;lt;/ItemTemplate&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;/asp:Repeater&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;/ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/asp:Repeater&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/table&amp;gt; &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;/span&gt; we can simply assign rpt1's DataSource to our DataSet, set its DataMember as "Customers", call DataBind(), and we are good to go:&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;rpt1.DataSource = ds;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;rpt1.DataMember = "Customers";&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;rpt1.DataBind();&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;/span&gt; A couple of more notes:
&lt;ol&gt;
    &lt;li&gt;The outer Repeater's DataItems are DataRowView objects, but the inner Repeater's DataItems are DataRows.  This is because we called the DataRowView's DataRow's GetChildRows() method, and not the DataRowView's CreateChildView() method.  I have found that this provides much better performance.  For small results, you may wish to call CreateChildView().&lt;br /&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;Instead of databinding in the HTML markup, you may wish to bind the inner Repeaters during the outer Repeater's DataBinding() event.  This keeps your HTML markup a little cleaner and shorter.&lt;br /&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;This function only works in .NET 2.0 or above, since it uses ToTable() method of the DataRowView class. &lt;/li&gt;
&lt;/ol&gt;
For more on nested repeaters, see:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://support.microsoft.com/default.aspx/kb/326338"&gt;http://support.microsoft.com/default.aspx/kb/326338&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://msdn2.microsoft.com/en-us/library/aa478959.aspx"&gt;http://msdn2.microsoft.com/en-us/library/aa478959.aspx&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185"&gt;http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185&lt;/a&gt;&lt;br /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
Note that all of those example use two separate SQL result sets.  Using the function provided, we can do the same with a single result.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/10/17/60375.aspx"&gt;Some HTML / ASP.NET Thoughts ...&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx"&gt;Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/03/28/60144.aspx"&gt;ASP.NET 1.1 -- Appsettings in Web.config&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2006/07/11/10572.aspx"&gt;Minimizing the ViewState for an ASP.NET 1.1 DataGrid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60391.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx</guid>
            <pubDate>Fri, 02 Nov 2007 16:16:21 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60391.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx#feedback</comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60391.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60391.aspx</trackback:ping>
        </item>
        <item>
            <title>Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</link>
            <description>A question I see very often in the SQLTeam forums is how to return data in a summarized form by concatenating a list of values into a single CSV column.  This can be done fairly easily in T-SQL, but as the formatting and concatenation requirements becomes more elaborate, be sure to ask yourself: Am I forcing presentation code into the database layer?  &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60359.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</guid>
            <pubDate>Tue, 09 Oct 2007 19:02:57 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60359.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60359.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60359.aspx</trackback:ping>
        </item>
        <item>
            <title>Using SQLBulkCopy to copy any .NET Object  to a SQL Server Table</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/05/22/sqlbulkcopy-any-dotnet-object.aspx</link>
            <description>In my &lt;a href="http://www.sqlteam.com/item.asp?ItemID=26941"&gt;SQLBulkCopy article&lt;/a&gt;, I mentioned that you can quickly copy data from anything that implements IDataReader to a SQL Server table using SQLBulkCopy (new in .NET 2.0).  In this &lt;a href="http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=83181"&gt;SQLTeam forum post&lt;/a&gt;, &lt;a href="http://www.elsasoft.org/"&gt;Jesse Hersch&lt;/a&gt; (jezemine) tells us that SQLBulkCopy only actually uses 3 methods of the interface.  This means that it is very easy to quickly create your own custom class which implements this interface to bulk copy pretty much any type of object collection or array to a SQL Server table.  He even provides code for a simple abstract  SqlBulkCopyReader class that you can inherit from to implement only the necessary parts of the interface.&lt;br /&gt;
&lt;br /&gt;
So, if you need to persist large amounts of data from your .net applications directly to SQL Server, and that data is not stored in DataTables, be sure to consider this option.  Thanks, Jesse !&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60214.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/05/22/sqlbulkcopy-any-dotnet-object.aspx</guid>
            <pubDate>Tue, 22 May 2007 18:40:10 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60214.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/05/22/sqlbulkcopy-any-dotnet-object.aspx#feedback</comments>
            <slash:comments>3</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60214.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60214.aspx</trackback:ping>
        </item>
        <item>
            <title>Using SQLBulkCopy to quickly transfer data from .NET to SQL Server</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/05/07/60196.aspx</link>
            <description>My latest article has just been posted over at &lt;a href="http://www.sqlteam.com/default.asp"&gt;SQLTeam.com&lt;/a&gt;: &lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;a style="font-weight: bold;" target="_blank" href="http://www.sqlteam.com/item.asp?ItemID=26941"&gt;Use SqlBulkCopy to Quickly Load Data from your Client to SQL Server &lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;.NET Framework 2.0 introduces a very handy new class in the System.Data.SqlClient namespace called SqlBulkCopy that makes it very easy and efficient to copy large amounts of data from your .NET applications to a SQL Server database. You can even use this class to write a short .NET application that can serve as a "middleman" to move data between database servers.&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
If you ever need to move large amounts of data to SQL Server from a .NET application, &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx"&gt;SQLBulkCopy&lt;/a&gt; is the way to go.  What's great about it is that you can use a populated DataTable as the source, or anything that implements &lt;a href="http://msdn2.microsoft.com/en-us/library/system.data.idatareader.aspx"&gt;IDataReader&lt;/a&gt;.  This means that you can use SQLBulkCopy to quickly move data from any OLEDB/ODBC datasource to SQL Server.&lt;br /&gt;
&lt;br /&gt;
Informal testing shows that it is about 4 times as fast as executing INSERT statements over and over when copying large amounts of data from a DataTable, and it certainly is much shorter and easier to write and maintain.&lt;br /&gt;
&lt;br /&gt;
I hope you find the article and the examples given useful.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.sqlteam.com/item.asp?ItemID=26941"&gt;Article link&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60196.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/05/07/60196.aspx</guid>
            <pubDate>Mon, 07 May 2007 13:29:46 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/05/07/60196.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60196.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60196.aspx</trackback:ping>
        </item>
        <item>
            <title>Regular Expression Replace in SQL 2005  (via the CLR)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/04/27/SQL-2005-Regular-Expression-Replace.aspx</link>
            <description>I had to do some data clean up the other day, and really needed some regular expression replacements to do the job.  Since .NET has a great RegularExpressions namespace, and since SQL 2005 allows you to integrate .NET CLR functions in your T-SQL code, I thought I'd go ahead and experiment with creating a RegExReplace() function.   &lt;br&gt;&lt;br&gt;&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/04/27/SQL-2005-Regular-Expression-Replace.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60190.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/04/27/SQL-2005-Regular-Expression-Replace.aspx</guid>
            <pubDate>Fri, 27 Apr 2007 20:20:43 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/04/27/SQL-2005-Regular-Expression-Replace.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60190.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60190.aspx</trackback:ping>
        </item>
        <item>
            <title>Sorting Columns with the C# Pivot Function</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/04/20/60182.aspx</link>
            <description>Time for another exciting edition of the &lt;a href="http://weblogs.sqlteam.com/jeffs/contact.aspx"&gt;mailbag&lt;/a&gt;!&lt;br /&gt;
&lt;br /&gt;
Maxime writes:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-style: italic;"&gt;Hi,&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;First of all, your class is really nice and it handles my problem &lt;/span&gt;&lt;span style="font-style: italic;"&gt;really well.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;I would like to ask you something about the class.  I would like to &lt;/span&gt;&lt;span style="font-style: italic;"&gt;order the pivot columns by column name but I'm unable to do that yet.  Do &lt;/span&gt;&lt;span style="font-style: italic;"&gt;you think you could help me with that ?&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;Thank you in advance for your time and sorry to bother you with this &lt;/span&gt;&lt;span style="font-style: italic;"&gt;question but I've tried to post in forums but didn't get any results.&lt;/span&gt;&lt;br style="font-style: italic;" /&gt;
&lt;br style="font-style: italic;" /&gt;
&lt;span style="font-style: italic;"&gt;Maxime &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
(Note: Maxime is referring to my simple Pivot function discussed &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2005/05/11/5101.aspx"&gt;here&lt;/a&gt;.  The idea is, as always, to let SQL focus on returning raw, summarized and filtered &lt;span style="font-style: italic;"&gt;data&lt;/span&gt; in a standard row/column result, and to let your very &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2005/05/12/5127.aspx"&gt;capable and efficient presentation layer&lt;/a&gt; handle all formatting -- which also happens to include pivoting data from rows to columns.)&lt;br /&gt;
&lt;br /&gt;
Sure thing, Maxime, that's a reasonable request and a great idea for improving the code.&lt;br /&gt;
&lt;br /&gt;
If you are using .NET 2.0, you can easily re-arrange columns in a DataTable, but this is not so easy in 1.1.  By using the setOrdinal method of a DataColumn, we can ensure that we are creating those new columns in the proper place.&lt;br /&gt;
&lt;br /&gt;
All you need to do is take the existing code for the Pivot function shown &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2005/05/11/5091.aspx"&gt;here&lt;/a&gt; and change just this line:&lt;br /&gt;
&lt;br /&gt;
&lt;p style="margin: 0in 0in 0pt; font-family: Courier New;" class="MsoNormal"&gt;      if (!tmp.Columns.Contains(s))&lt;/p&gt;
                      &lt;span style="font-family: Courier New;"&gt;tmp.Columns.Add(s, dataValues.GetFieldType(pValIndex));&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
To:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;if (!tmp.Columns.Contains(s))&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;{&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;  DataColumn c = tmp.Columns.Add(s, dataValues.GetFieldType(pValIndex));&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;  // set the index so that it is sorted properly:&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;  int newOrdinal = c.Ordinal;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; &lt;span style="font-weight: bold;"&gt; for (i = newOrdinal - 1; i &amp;gt;= dataValues.FieldCount - 2; i--)&lt;/span&gt;&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;    if (c.ColumnName.CompareTo(tmp.Columns[i].ColumnName) &amp;lt; 0)&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;      newOrdinal = i;&lt;/span&gt;&lt;br style="font-family: Courier New; font-weight: bold;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;  c.SetOrdinal(newOrdinal);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; font-weight: bold;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That should result in "sorted" pivot columns.  Use similar tricks if necessary to move other columns around in any DataTable as needed.  To sort the columns in descending order, change the CompareTo() method to compare with &amp;gt; 0.  In addition, keep in mind that this is a regular text comparison; if you need to sort numeric or date column headers, you should first convert those strings to proper datatypes before doing the compare, or at the very least to a string format that compares and sorts you way you'd like.  &lt;br /&gt;
&lt;br /&gt;
If you are using .NET 1.1, your only options are to a) move the data from this DataTable to &lt;span style="font-style: italic;"&gt;another&lt;/span&gt; one, with columns that are properly sorted as the new table is created, or b) gather the column names in advance and pass those to the function and create the DataTable with those columns already added and ready to be filled in the proper order.&lt;br /&gt;
&lt;br /&gt;
I hope this helps, and congratulations on writing simple, clear, short and efficient SQL code while letting your presentation layer do its job!&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;br /&gt;
&lt;/span&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2003/10/24/363.aspx" title="View Entry"&gt;Keep those SQL Crosstabs flexible!&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/11/5101.aspx" title="View Entry"&gt;.NET CrossTabs: Transforming a Normalized DataReader into a Pivoted DataTable&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/12/5127.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl04_HyperLink1"&gt;.NET CrossTabs versus SQL Server CrossTabs&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2005/05/15/5175.aspx" title="View Entry"&gt;ASP and ADO Pivots -- Old School CrossTabs&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/07/12/60253.aspx" title="View Entry"&gt;SQL 2005 PIVOT Operator (link)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60182.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/04/20/60182.aspx</guid>
            <pubDate>Fri, 20 Apr 2007 16:05:42 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/04/20/60182.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60182.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60182.aspx</trackback:ping>
        </item>
        <item>
            <title>ASP.NET 1.1 -- Appsettings in Web.config</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx</link>
            <description>It's great to be able to put settings in the Web.Config file for my ASP.NET projects.  The problem for me, though, is that when I use System.Configuration.ConfigurationSettings.AppSettings(name)
to return a setting that doesn't exist in the file ...
&lt;br&gt;&lt;br&gt;
&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx"&gt;read more&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60144.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx</guid>
            <pubDate>Wed, 28 Mar 2007 16:09:16 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60144.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60144.aspx</trackback:ping>
        </item>
        <item>
            <title>Always Use Parameters. Even if you don't  use Stored Procedures.</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx</link>
            <description>Let's assume that for some reason you are not using Stored Procedures. While I can respect your choice in that regard, that doesn't mean that you cannot still use parameters when constructing your SQL statements at the client. 
&lt;br&gt;
&lt;br&gt;
&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/10728.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx</guid>
            <pubDate>Fri, 21 Jul 2006 14:43:00 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2006/07/21/10728.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/10728.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/10728.aspx</trackback:ping>
        </item>
    </channel>
</rss>