Enter LINQ-to-SQL
After evaluating Typed DataSets, I decided that they are handy for quick demos of data-binding mechanics, but they don't have anything close to the set of robust features I want from a data-access tier. My data-access tier should be extensible, flexible, elegant, efficient, and create easy-to-read and maintain code. Typed DataSets meet some of those criteria, but not all. The Typed DataSet code for the original project (which we called "Moniker") was shelved and we started over with a new project: "Moniqer."
The next evolution above Typed DataSets is LINQ-to-SQL. Language Integrated Query (LINQ) is an update to C# that provides lots of new functionality. This functionality is available to you whether you use LINQ or not and includes the ability to extend existing, sealed classes with your own static language extension classes. These language extensions are the foundation on which LINQ and LINQ-to-SQL are built. Whether you're working with databases or simple arrays, LINQ will dramatically increase your productivity and reduce the complexity of the code you write. LINQ queries are written in C# or VB.NET (there is currently no LINQ support for other languages) and follow a syntax that is similar to the XQuery FLWR (For-Let-Where-Return) syntax that looks something like the following:
var query = from [variable] in [source set] group [group syntax] where [criteria] orderby [column syntax] select [variable | projection]
The [source set] can be anything from an array or a collection to a class generated by the SQLmetal.exe command-line schema tool that represents a database or a data table fronting a back-end relational data store such as SQL Server 2005. For example, the following query returns the title and URL of every bookmark in the database, ordered by title, projected into a dynamically generated anonymous class:
var bookmarks = from bookmark in Database.Bookmarks orderby bookmark.Title select new { bookmark.Title, bookmark.Url };
The bookmarks variable won't actually contain any results at this point. The results are not retrieved until some client code attempts to traverse the results by calling GetEnumerator(), which happens automatically during a foreach loop like this:
foreach ( var bookMark in bookmarks ) { Console.WriteLine("<a href=\"{0}\">{1}</a>", bookMark.Url, bookMark.Title); }
There is a lot more power bottled up inside LINQ and LINQ-to-SQL and you should start working with this new technology as soon as you can. The other powerful feature of LINQ that I mentioned was language extensions, which give you the ability to dynamically extend any class by adding new members. Take a look at the following language extension written for a tool that consumes RSS feeds that sometimes contain missing elements:
public static class LanguageExtender { public static string SafeValue(this XElement input) { return (input == null) ? string.Empty : (string)input.Value; } public static DateTime SafeDateValue(this XElement input) { return (input == null) ? DateTime.MinValue : DateTime.Parse(input.Value); } }
The preceding code actually extends the LINQ-to-XML class XElement by adding two methodsSafeValue and SafeDateValueso that you can simply type myElement.SafeValue() and it automatically takes care of the null-value condition.
You can do even more powerful things such as retrieve a set of parent rows, then iterate through them, dynamically retrieving the child rows by traversing a foreign key relationship in an on-demand fashion:
foreach ( Bookmark b in allBookmarks) { Console.WriteLine(b.Title); foreach (Tag t in b.Tags) { Console.Write("{0} ", t.Title); } Console.WriteLine(); }
What CSS Zen can do for the elegance, flexibility, and beauty of your HTML, LINQ-to-SQL can do for your C# data access layer! I like to think of LINQ-to-SQL as "Data Zen."