XLinq Part 2: Using Standard Query Operators

XLinq Part 2: Using Standard Query Operators

n Part 1 of this article series, you looked at XLinq’s ability to create, modify, and delete XML data. Building on that background, this installment focuses on XLinq’s query features, by first delving into the standard query operators supplied with LINQ and then providing examples of using them to query XML data. Finally this article will also provide an example of transforming XML data to a collection using the new LINQ query format.

Querying XML
The main goal of the LINQ family of technologies is to add general purpose query facilities to the .NET Framework so that you can query all sources of information (including the relational and XML data) through a unified query approach. Because of the generic query foundation, you can apply LINQ to query any class as long as that class implements IEnumerable class. This also means that you can potentially leverage LINQ from any .NET language (it’s currently being added to C# and VB.NET) because the underlying LINQ API is nothing but a set of .NET classes.

XLinq is an XML query language that inherits from the LINQ query foundation. You can use it to query XLinq objects such as XElement, XDocument, etc using LINQ query facilities.

Performing a Simple Query
Here’s a simple XML file called Products.xml (available with the downloadable sample code) that I’ve used as the source document for the query capabilities described in this article:

               Adjustable Race     AR-5381             Bearing Ball     BA-8327             BB Ball Bearing     BE-2349                LL Crankarm     CA-5965     Number="" />         ML Crankarm     CA-6738             ML Crankarm     CA-6738       

The Products.xml file is a simple product list where each product consists of an id, name, and number. To query this file with XLinq, first create an ASP.NET page named SimpleXLinqQuery.aspx, and then set the page’s contents as shown below:

                                     Simple XLinq Query         

At the top of the page, you can see import statements for all the required namespaces. One key namespace is System.Query, which contains a number of powerful classes that provide native querying capabilities. This example shows how to return all the product names in the Products.xml file.

Add a button control and a GridView control to your ASP.NET page. In the Click event code for the button, start by loading the XML file into memory using the Load() method of the XElement class.

   XElement products =      XElement.Load(Server.MapPath   ("~/App_Data/Products.xml"));           

After loading the XML file, the next step is to retrieve all the product names.

   var productNames =  from prod in       products.Elements("Product")      orderby (string) prod.Element("Name")      select (string) prod.Element("Name");   

The preceding line of code does the majority of the work. Here’s a more detailed examination.

Figure 1. Product Names Output: Here’s the output of the SimpleXLinqQuery.aspx example, showing the list of product names retrieved from the Products.xml file.

The var keyword signals the compiler that you are using the new local variable Type Inference feature in .NET Framework 3.0. This var keyword differs from the JavaScript var keyword (which creates a variable capable of holding any type of data) in that it tells the C# compiler to infer the type from the assignment of values. As an example, if you assign “1” to a variable of type var, the compiler will infer the type of that variable to be int

On the right side of the equals sign, the code uses a syntax to query the XML file. This statement selects all elements from the elements within the Products.xml file and returns them as an enumerable String collection of product names. The orderby clause orders the items within the collection by the contents of the Name element, which is typecast into a string before the results are ordered.

Once you have the list of product names, you can easily bind it to a GridView by setting the DataSource property (see Figure 1).

      gridResult.DataSource = productNames;   gridResult.DataBind();

Using the “where” Operator
Beyond the basic query operators you just saw, XLinq has operators such as the important where operator that lets you filter query results based on supplied criteria. To demonstrate this, add a text box named txtValue and a button named btnGetProductNameForID to the page. As the name of the button suggests, clicking this button retrieves the product name for a specific product ID. The Click event code for the new button looks like this:

Figure 2. Displaying a Product Name for a Specific Product ID: In this example, when a users enters a product ID and clicks the button, the application returns the corresponding product name.
   void btnGetProductNameForID_Click     (object sender, EventArgs e)   {              XElement products = XElement.Load           (Server.MapPath         ("~/App_Data/Products.xml"));                 var productNames =  from prod in       products.Elements("Product")      where (string)prod.Attribute("ID")          == txtValue.Text                                    select (string) prod.Element("Name");         gridResult.DataSource = productNames;      gridResult.DataBind();           }

In this example, when a user enters a product ID into the txtValue text field, and clicks button, the “where” operator selects the product name that matches the supplied product ID. Figure 2 shows the example in action.

Using the “Take” Operator
Similar to the where operator, the Take operator provides you with a way to select all the elements before some supplied position. For example, the following code snippet shows how to select the first five product names from the Products XML file.

Author’s Note: Although the syntax for the “where” operator is different from the “Take” operator?which is obviously a method?the documentation refers to both as “operators,” so I have followed that lead in the terminology used for this article.

   void btnGetTop5ProductNames_Click      (object sender, EventArgs e)   {
Figure 3. Filtering the First Five Product Names: Here's the page in a browser, displaying the first five product names from the products list.
XElement products = XElement.Load (Server.MapPath ("~/App_Data/Products.xml")); var productNames = (from prod in products.Elements("Product") orderby (string) prod.Element("Name") select (string) prod.Element("Name")).Take(5); gridResult.DataSource = productNames; gridResult.DataBind(); }

I won’t go through the full description of how to set up an example page, but you can easily see what’s involved by looking at Figure 3.

Note how adding the Take operator at the end of the query simply changes the output returned by the query. This just shows the power of LINQ as a query language.

Similarly, you can also get the unique list of product names using the Distinct operator.

   var productNames =      (from prod in    products.Elements("Product")     orderby (string) prod.Element("Name")   select (string)prod.Element   ("Name")).Distinct();

The Distinct operator eliminates duplicate product names from the resultant output. In addition to the Where, Take, and Distinct operators shown so far, LINQ exposes a number of other operators, the most important of which are shown in Table 1.

Table 1. Standard Query Operators: The table shows common query operators used for querying objects that are based on IEnumerable.

Operator Description
select Specifies the return value for the query.
where Allows you to filter the output.
First Retrieves the first member from the resultant list.
ElementAt Allows you to access the specific member at specified position.
Take Accesses the members before the specified position.
Skip Accesses the members after the specified position.
ToDictionary Allows you to return the results in a key pair value list.
orderby Allows sorting of the resultset in ascending order.
orderbyDescending Allows sorting of the resultset in descending order.
Reverse Allows you to reverse the order of sequence.
Distinct Allows you to filter duplicate members.
ToList Allows you to return the results as a list represented by List.
Except Allows you to filter elements that are members of a specific set.

Transforming XML with XLinq
So far, you’ve seen how to query the XML file using query operators, but sometimes you might want to query the XML and transform the results directly into objects that you can then pass to other applications or services for further processing. With XLinq, converting XML to objects is a breeze, often requiring only a single line of code.

To demonstrate this, I’ll show you how to query the Products.xml file and transform its contents into a collection of ProductInfo objects that are then bound to a GridView control. Listing 1 shows the complete code for the page, but here’s the interesting portion. First, to retrieve all the elements inside the element, use the XElement object’s Descendants() method:

   var productInfos =  from prod in      products.Descendants("Product")      orderby (string) prod.Element("Name")      select new ProductInfo      {         ProductID = (int)prod.Attribute("ID"),         ProductName = (string)prod.Element("Name"),       ProductNumber = (string)prod.Element("Number")                                      };

After you have all the elements, you then use those values to populate the ProductInfo collection. Here’s the neat part: You can instantiate the ProductInfo objects and set their public properties as part of the select operator, using the new object initialization syntax available in the .NET Framework 3.0. Through this new syntax, you can instantiate a new object, set its properties to appropriate values, using?as promised?a single line of code.

As you can probably guess, the ProductInfo class used in the preceding example is very simple. It exposes three public properties: ProductID, ProductName, and ProductNumber.

   using System;   public partial class ProductInfo   {      private int _productID;      private string _productName;      private string _productNumber;         public ProductInfo(){}         public int ProductID      {         get{return _productID;}         set{_productID = value;}      }   
Figure 4. XML-to-object Conversion: Here's the output produced by converting the products XML data to a ProductInfo collection and binding that to a GridView Control.
public string ProductName { get{return _productName;} set{_productName = value;} } public string ProductNumber { get{return _productNumber;} set{_productNumber = value;} } }

Place the ProductInfo class in the App_Code directory of your project, and then navigate to the page using the browser. You’ll see the output shown in Figure 4.

In this installment of the XLinq series, you’ve seen how to use the standard LINQ query operators to select, and filter XML data, as well as how to transform XML data into a strongly-typed collection. The next installment focuses on using DLinq (LINQ to SQL) in conjunction with XLinq to retrieve relational data, transform it to XML data, and vice versa.


Share the Post: