RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


The Baker's Dozen: A 13-Step Crash Course for Using LINQ : Page 4

Start becoming familiar with the different areas of LINQ with the Microsoft Visual Studio "Orcas" Beta.

Tip 8: LINQ to XML
The code below shows some basic examples of creating and querying XML data. You can create XML file contents easily using the XElement and Xattribute objects, and create a queryable object using XDocument. This illustrates the point that those who don't plan on using some aspects of LINQ (such as LINQ to SQL) can still use other LINQ functionality.

   XElement XmlAddr = new XElement("Addresses",
   new XElement("AddressRec",
   new XAttribute("EmployeeID",1),
   new XElement("FirstName","Kevin"),
   new XElement("LastName", "Goff"),
   new XElement("Address","111 Main Street"),
   new XElement("City","Philadephia"),
   new XElement("State","PA"),
   new XElement("Zip","11111")),
   new XElement("AddressRec",
   new XAttribute("EmployeeID",2),
   new XElement("FirstName","John"),
   new XElement("LastName", "Brown"),
   new XElement("Address","22 4th Ave"),
   new XElement("City","Baltimore"),
   new XElement("State","MD"),
   new XElement("Zip","22800")),
   new XElement("AddressRec",
   new XAttribute("EmployeeID",3),
   new XElement("FirstName","Jason"),
   new XElement("LastName", "Wilson"),
   new XElement("Address","11 Baltimore Street"),
   new XElement("City","Baltimore"),
   new XElement("State","MD"),
   new XElement("Zip","22678")));
   XDocument oDoc = new XDocument(XmlAddr);
   var xResults = 
      from oXml in oDoc.Descendants("AddressRec")
      where (string)oXml.Element("State") == "MD"
      orderby (string)oXml.Element("Zip")
      select oXml;
Tip 9: LINQ to DataSets
The beauty of LINQ is that it offers something for just about everyone. Even if you continue to opt for stored procedures instead of LINQ to SQL, LINQ still provides better capabilities for working with the result sets.
While ADO.NET has always had the ability to filter the contents of a DataTable with the Select method, the biggest querying limitation of the DataTable was its inability to perform a JOIN (or any related set-based operation). You could simulate a JOIN by using Relation objects and Parent/Child syntax, but that capability paled in comparison to standard SQL JOIN operations. Fortunately, you can use the same LINQ syntax to query DataTables as databases.

For example, you can use LINQ to DataSets to query an enumeration of DataRows consisting of of either typed or untyped DataSets. You can also perform SELECT DISTINCT, UNION, JOIN, and GROUP operations.

Note two things in the code below. First, the code references the extension method AsEnumerable, from the System.Data.EntityClient namespace. This allows you to use LINQ syntax to query the DataTable. Second, the code also uses the Field<datatype> syntax to convert each DataColumn (which .NET stores as an object) to the appropriate datatype.

   DataTable dtEmps = new DataTable();
   // Query for name = Kevin or Salary > 90Kvar 
   // Query = 
         from rowData in dtEmps.AsEnumerable() 
         where rowData.Field<string>("FirstName") ==
          "Kevin" || rowData.Field<decimal>("Salary") > 90000
         select rowData;
If you use strongly-typed DataSets (which I recommend over untyped DataSets), you can reference columns directly:

   var Query = 
      from rowData in dtEmps.AsEnumerable() 
      where rowData.FirstName == "Kevin" 
         || rowData.Salary > 90000
   select rowData;
Finally, as I mentioned, you can apply JOIN and GROUP syntax to ADO.NET DataTables. Suppose you have two strongly-typed DataTables, one that stores a list of Employees (dtEmployees, storing EmpPK and Name), and a second that stores daily labor information (dtLabor, storing multiple rows by EmpPK for WorkDate and WorkHours). You want to join the two tables to produce a summary result of hours for each employee for the month of February 2007:

   var LaborSum = 
      from Hours in oData.dtLabor 
      where Hours.WorkDate.Year == 2007 &&
         Hours.WorkDate.Month == 2 
      group Hours by Hours.EmpPK into g
      select new {EmpPK=g.Key, 
         SumHours = 
         g.Sum(o => o.WorkHours)} into HourSum
      orderby HourSum.SumHours descending
      join Master in oData.dtEmployees on
          HourSum.EmpPK equals Master.EmpPK
      select new {Master.Name, HourSum.SumHours};
Tip 10: Lambda Expressions
If you're still feeling left out, because you work primarily with data stored in custom collections, here's something for you: You can use LINQ capabilities to query your collections.

Automatic properties are like getting clothes for presents—they might not seem very exciting, but you'll find them useful.
In past articles on .NET Generics in Visual Studio 2005, I've shown how to sort and filter custom collections using anonymous methods inside the FindAll and Sort methods of the List class. Suppose you've created a simple Customer class, with properties for CustomerID and Name, LocationID, and AmountDue. The following two code snippets show how you'd filter an existing collection of customers for Locations 1 and 2, where the AmountDue is greater than 15K, and then perform a descending sort on AmountDue by location:

   // VS2005 syntax
   List<CustomerRec> oFilteredCustomers =
         delegate(CustomerRec oRec)
            return ((oRec.LocationID == 1 
               || oRec.LocationID == 2)
               && oRec.AmountDue > 15000);
   // VS2005 syntax
      (delegate(CustomerRec oRec1, CustomerRec oRec2)
         return oRec1.LocationID == oRec2.LocationID ?
            oRec2.AmountDue.CompareTo(oRec1.AmountDue) :
In Orcas Beta 1, there's good news, and then there's great good news. First, the good news: the new lambda expression capability in Orcas Beta 1 allows you to rewrite the above snippets for better readability by eliminating the need to explicitly specify the delegate parameter. Think of lambda expressions as a simplified form of anonymous methods. In the code below, all you need to do is specify the parameter for FindAll (I've called it oRec, which .NET automatically knows is of type CustomerRec), and then use the "token set" symbol (=>) that .NET uses to separate the parameter from the actual expression):

   // Lambda expression, more concise
   List<CustomerRec> oFilteredCustomers =
      oCustomerRecs.FindAll(oRec =>
      // Note that oRec is automatically
      // of type "CustomerRec"
      (oRec.LocationID == 1 ||
      oRec.LocationID == 2)  &&
      oRec.AmountDue > 15000);
In the sorting example (which receives two parameters), you need only separate the two comparison objects (oRec1, oRec2) by commas, include the token set symbol (=>), and then write the expression. Much simpler than anonymous methods! I'll talk more about lambda expressions in Tip 12.

   // Lambda expression, more concise
   oFilteredCustomers.Sort((oRec1, oRec2) =>
      oRec1.LocationID == oRec2.LocationID ?
      oRec2.AmountDue.CompareTo(oRec1.AmountDue) :
So that's the good news. Now for the great news—you can also apply LINQ query syntax to object collections (see the next tip).

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date