Extending Typed DataSets - Sorting Child Rows
As you're no-doubt aware, strongly-typed DataSets in ADO.NET provide you with a whole bunch of useful classes with properties and methods that replace the ordinary way you might locate or retrieve rows.
One such class is a class is [DataTable]Row (where [DataTable] is the name of your DataTable), which extends DataRow.
If you have defined DataRelations between tables, the [DataTable]Row class has a method for each DataRelation called Get[RelatedDataTable]Rows() (where [RelatedDataTable] is the table that [DataTable] relates to), which returns all the rows that relate to the specified [DataTable] row.
Look, all these class names are getting very confusing, so I'll give you a more realistic example.
Let's say we have a DataTable called Authors, and another called Books. Between those two tables is a DataRelation such that each author can have zero or more books.
A strongly typed DataSet containing these tables would give you a new class called AuthorsRow (derived from DataRow), and another called BooksRow (also from DataRow).
AuthorsRow has a method called GetBooksRows(), which returns an array of BooksRow objects - one for each of the author's books. So we can write some tricky code like this:
foreach (CollectionDataSet.BooksRow book in currentAuthor.GetBooksRows()) { // do something with book } Too easy!
But what if we want to sort those BooksRow objects in the order in which they were published? Ordinarily we'd have to abandon our use of strongly-typed methods and fall back on the tried-and-true Select() method, to we could pass a parameter to specify the order in which rows are returned. This is all well and good, but Select() returns an array of DataRow objects, not BooksRow objects. Plus it means that the caller needs to know column names.
With new ADO.NET 2.0 DataSet classes we can sort the array returned by GetBooksRows() using Array.Sort() and a custom comparer delegate or method:
CollectionDataSet.BooksRow[] books = currentAuthor.GetBooksRows(); Array.Sort(books, delegate(CollectionDataSet.BooksRow x, CollectionDataSet.BooksRow y) { return x.PublishedDate.Subtract(y.PublishedDate).Seconds; } ); Looks scary, but it's really not. We're providing an anonymous delegate that takes two BooksRow objects and compares them by returning an int - much the same way that String.Compare() works.
This is all well and good, but it's cumbersome to have to code this up every time you call GetBooksRows(). Thankfully there's a nice way around that, too.
When we open our typed DataSet's code, we get a skeleton class, something like this:
public partial class CollectionDataSet { } All the real work is done in a second file (ending in .Designer.cs) ... which leaves this skeleton class for us to play with. Every class defined by the strongly-typed ADO.NET 2.0 DataSet is a partial class that we can extend.
Let's add a new method to our AuthorsRow class:
public partial class CollectionDataSet { public partial class AuthorsRow { public BooksRow[] GetBooksRows(bool sorted) { BooksRow[] books = GetBooksRows(); if (sorted) { Array.Sort(books, delegate(BooksRow x, BooksRow y) { return x.PublishedDate.Subtract(y.PublishedDate).Seconds; } ); } return books; } } } Oila! Now we can call our new AuthorsRow method, also called GetBooksRows(), with bool parameter to specify that we want the rows sorted. Our new code looks like this:
foreach (CollectionDataSet.BooksRow book in currentAuthor.GetBooksRows(true)) { // do something with book } The use of partial classes in strongly-typed DataSets provides a great extensibility mechanism for tricks like this. If you find yourself extending the ADO.NET 2.0 DataSet class to simplify your life like I have, I would love to hear from you!
ps. Thanks to Dave Burke for his proof-reading of this article.

Comments
#
11/09/2006 2:29 PM
How weird. I am learning strongly typed dataset and I did a google search and what do you know? I get sent back to Mabster!
# Paulo
10/10/2006 11:03 PM
What about extending the code generation in a custom tool?.
BR,
(paulovila@gmail.com)
# mabster
11/10/2006 6:51 AM
That would be an awesome trick, Paulo, but then I kind of like the control you have when you use partial classes. If you know (or discover) how to create custom code-generators like you describe then please post about it and I'll link to you!
# Kilik
26/04/2007 3:00 AM
This is a sweet one. Implemented it for my application and it works like a dream. Thanks!!!
Leave a Comment