Chapter 23
LINQ to XSD Supports Typed XML Programming

In This Chapter

Image Understanding the Basic Design Goals of LINQ to XSD

Image Programming with LINQ to XSD

“Necessity is the mother of invention, it is true, but its father is creativity, and knowledge is the midwife.”

—Jonathan Schattke

When the CodeDOM was invented, it was mentioned in several articles that the CodeDOM would end up being an important part of the .NET Framework that would let developers create new and imaginary things. (If you don’t know, the CodeDOM supports code-generating source code and emitting running binaries.) And, the CodeDOM has been quietly playing an important role in the background since .NET’s inception and it plays a role here, too.

LINQ to XSD—also referred to as LINQ to XML for Objects—is typed programming support for LINQ to XML. LINQ to XML is based on the XDocument and XElement classes and supports LINQ over XML queries. LINQ to XSD is an extension to LINQ to XML that permits you to program with LINQ to XML but explicitly leave out the calls to the XDocument and XElement methods. For example, LINQ to XML support queries like this:

    (from item in purchaseOrder.Elements(“Item”)
     select (double)item.Element(“Price”)
          * (int)item.Element(“Quantity”)
    ).Sum();

LINQ to XSD supports querying the same XML document but with a more natural, IntelliSense supported and object-oriented style of writing the LINQ queries. With LINQ to XSD, the preceding query can be written as follows:

    (from item in purchaseOrder.Item
     select item.Price * item.Quantity
    ).Sum();

Notice that the typecasting is gone, as well as the explicit method calls to methods in the System.Xml.Linq namespace, like Elements.

LINQ to XSD uses XDocument as its backing store instead of the XSD.exe utility’s use of XmlDocument. Thus, the calls are made, but the LINQ to XML plumbing is code generated for you into typed, wrapper objects that handle the LINQ to XML plumbing calls.

It is important to note that this technology is early development and likely to change, but this chapter shows you a little about the basic design goals and walks through an example based on the Alpha 0.2 release from February 2008.

Understanding the Basic Design Goals of LINQ to XSD

A popular concept (apparently at Microsoft at least) right now is the concept of mitigating impedance mismatches. You can think of impedance mismatches as productivity speed bumps. For example, when twisting database tables into objects, programmers are impeded because the shape of normalized tables and object hierarchies seldom match. The impedance mismatch for XML to objects is getting the data from XML text documents into objects and doing something with the data in an object-oriented way. For example, in LINQ to XML, everything is a string, so you have to typecast. Nodes have to be requested by chaining method calls. All this plumbing impedes forward motion—a speed bump.

The basic design objective of LINQ to XSD (or LINQ to XML for objects) is to hide the XML plumbing, so the developer can focus on desired outcomes, for example, summing the cost of an order.

Some basic observations about LINQ to SQL (paraphrasing the present literature on the subject) are that the shape of the untyped code is preserved in the typed code. In the fragment in the chapter opener, you see that the general structure of the LINQ to XML query didn’t really change that much in the LINQ to XSD version. Untyped, string-encoded access is eliminated with LINQ to XSD, removing the need for typecasting everything that is not to be treated as a string. In addition, XML namespaces are replaced by proper common Language Runtime (CLR) namespaces. This means you don’t have to concatenate string namespaces, but rather you use instance-oriented mapping; that is, LINQ to XSD generated types have namespaces and classes and the member-of dot-operator (.) resolves these references.

The general design goals are to cover all of XML Schema. Create mappings that are predictable and comprehensible, facilitate round-tripping, avoid relying on customizations, convey schema intent into the object models as much as possible, and derive classes that are as close to the expectations of the programmer as possible.

In an alpha product, objectives are likely to change and some of the objectives might not be complete yet, but the general idea of making LINQ to XML more natural by eliminating the XML plumbing is probably a safe bet.

Programming with LINQ to XSD

You can try the samples in the LINQ to XSD download, and a new one was created for this book. The example in this section uses a list of luncheon menu items from Dusty’s Cellar in Okemos and, using LINQ to XSD, converts the XML to typed objects and a LINQ query is written against the code-generated objects and the underlying XML file.

From 1,000-foot view, the following are the steps for using LINQ to XSD:

1. Download and install the LINQ to XSD preview.

2. Create a LINQ to XSD Preview Console Application.

3. Define the content of an XML file.

4. Define the content of an XML Schema file.

5. Compile the project to generate the typed XML wrapper classes.

6. Query against the generated code with LINQ.

Downloading and Installing the LINQ to XSD Preview

You can stay on top of the latest developments for LINQ to XSD at Microsoft’s XML Team WebLog at http://blogs.msdn.com/xmlteam/archive/2008/02/21/linq-to-xsd-alpha-0-2.aspx. To download and install the Alpha 0.2 preview used for the demo in this chapter, you can go to this link http://www.microsoft.com/downloads/details.aspx?FamilyID=a45f58cd-fcfc-439e-b735-8182775560af&displaylang=en, Google for LINQ to XSD Preview, or follow the link at the XML Team’s WebLog. (Basically, www.microsoft.com/downloads/ will get you most of the way there for almost any Microsoft product.)

The installation process is a standard install process. Like most of the Microsoft Visual Studio add-ins, the LINQ to XSD installation adds project templates to the Visual Studio folder, and these elements show up in the Add New Item and Projects dialog boxes as selectable templates. (For more information on building templates, check out my article “Creating Project Templates in .NET” at http://www.informit.com.)

Creating a LINQ to XSD Preview Console Application

JavaScript files, a common Wizard DLL, wizard (*.vsz), and Visual Studio directory (*.vsdir) files all work together to support all of the various project templates. Unless you are writing custom wizards, you can let Visual Studio manage all this plumbing and infrastructure.

From an examination of a LINQ to XSD project, it looks like all the new template does is add a reference to the Microsoft.Xml.Schema.Linq.dll assembly, but in general, it seems to work best to let the various wizards create project templates for you. To create a LINQ to XSD Preview project, follow these steps:

1. Open Visual Studio (after installing the LINQ to XSD Preview).

2.Select File, New Project, LINQ to XSD Preview, LINQ to XSD Console Application to create a LINQ to XSD console project.

The net result is that you will have a basic console application with a reference to the LINQ to XSD assembly.

Defining the XML Context

The next step is to define some XML content. Spending a fair amount of my meager disposable income on cigars and tasty tidbits from Dusty’s Cellar in Okemos, the sample XML in this section contains the luncheon menu for the restaurant.

Select Project, Add New Item and add an XML file to the console application project. Figure 23.1 shows the Add New Item dialog box with the XML File template selected and Listing 23.1 shows the XML file created from the menu items.

Figure 23.1 The Add New Item template dialog box is your friend.

Image

Listing 23.1 A Sample XML File Describing a Luncheon Menu

<?xml version=“1.0” encoding=“utf-8” ?>
<Dustys xmlns=“http://www.dustyscellar.com/TuscanLuncheon”>
  <Luncheon>
    <LuncheonId>L1</LuncheonId>
    <Price>12.00</Price>
    <LimitedByDayOfWeek></LimitedByDayOfWeek>
    <Item>
      <Name>Chicken Caesar Salad</Name>
      <Description>Tossed in mixed greens with Key Lime Caesar dressing</Description>
    </Item>
    <Item>
      <Name>Salmon Salad</Name>
      <Description>With assorted vegetables and Balsamic Vinaigrette</Description>
    </Item>
    <Item>
      <Name>Torta Rustica</Name>
      <Description> Our signature dish made with brioche 
→ dough filled with smoked turkey, red peppers, provolone,
→ Gournay cheese and artichoke hearts</Description>
    </Item>
  </Luncheon>
  <Luncheon>
    <LuncheonId>L2</LuncheonId>
    <Price>21.00</Price>
    <LimitedByDayOfWeek></LimitedByDayOfWeek>
    <Item>
      <Name>Chicken Breast</Name>
      <Description>Stuffed with herb garlic cheese,
→ sun dried tomatoes and spinach</Description>
    </Item>
    <Item>
      <Name>Grilled 8oz Sirloin Steak</Name>
      <Description>With Pinoit Noir demi glaze</Description>
    </Item>
    <Item>
      <Name>Spinach Linguine</Name>
      <Description>With Tarragon Marsala cream, bacon,
→wild mushrooms, shallots and Romano cheese</Description>
    </Item>
  </Luncheon>
  <Luncheon>
    <Luncheon>F</Luncheon>
    <Price>12.00</Price>
    <LimitedByDayOfWeek>Sunday</LimitedByDayOfWeek>
    <Item>
      <Name>French Toast</Name>
      <Description>Stuffed with seasonal berries</Description>
    </Item>
    <Item>
      <Name>Scrambled Eggs</Name>
      <Description>With bacon or sausage</Description>
    </Item>
    <Item>
      <Name>Spinach Crepe</Name>
      <Description></Description>
    </Item>
    <Item>
      <Name>Torta Rustica</Name>
      <Description> Our signature dish made with brioche
→ dough filled with smoked turkey, red peppers, provolone,
→ Gournay cheese and artichoke hearts</Description>
    </Item>
  </Luncheon>
</Dustys>

Any XML file will do. The key to using LINQ to XSD is to define an XML Schema file, its contents, and set the build option to use the LinqToXSD.exe compiler that is downloaded with the LINQ to XSD Preview package.

Defining the XML Schema File

Again, return to the Add New Item dialog box and add an XML Schema file to your project. An XML file defines the data in a self-describing way. The XML Schema defines the schema or structure of the XML file. The separation of these two items supports changing the structure of the XML data by applying different schemas.

XML Schema files are generally a high level of abstraction that describes what XML documents conforming to a particular schema should contain. Schema documents support adding additional information above and beyond what an XML document itself will support, like adding regular expressions. Listing 23.2 contains a schema file for our example.

Listing 23.2 An XML Schema File (.xsd) for the Tuscan Luncheon Menu Data in the XML File in Listing 23.1

<xs:schema
  xmlns:xs=“http://www.w3.org/2001/XMLSchema
  targetNamespace=“http://www.dustyscellar.com/TuscanLuncheon
  xmlns=“http://www.dustyscellar.com/TuscanLuncheon
  elementFormDefault=“qualified”>

  <xs:element name=“Dustys”>
    <xs:complexType>
      <xs:sequence>
        <xs:element ref=“Luncheon”
                    minOccurs=“0” maxOccurs=“unbounded”/>
     </xs:sequence>
   </xs:complexType>
  </xs:element>

  <xs:element name=“Luncheon”>
    <xs:complexType>
      <xs:sequence>
        <xs:element name=“LuncheonId” type=“xs:double”/>
        <xs:element name=“Price” type=“xs:double”/>
        <xs:element name=“LimitedByDayOfWeek” type=“xs:string”/>
        <xs:element ref=“Item”
                    minOccurs=“0” maxOccurs=“unbounded”/>
     </xs:sequence>
   </xs:complexType>
  </xs:element>

  <xs:element name=“Item”>
    <xs:complexType>
      <xs:sequence>
        <xs:element name=“Name” type=“xs:string”/>
        <xs:element name=“Description” type=“xs:string”/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

The XSD Schema file indicates that documents that support this schema contain an element Dustys. Dustys contains a list of luncheon items, and the luncheon items contain a LuncheonId, Price, LimitedByDayOfWeek, and an Item element. The schema also defines the data types for these elements. Item is defined as containing a Name and Description.

Adding a Regular Expression to a Schema File

If you wanted to further constrain XML files to those that match the schema, you could add a regular expression that enforces values for the LimitedByDayOfWeek element. As defined in Listing 23.2, any string could be placed in this element, but what you really want are valid days of the week or perhaps an empty string (refer to Listing 23.3).

Listing 23.3 Defining a Regular Expression Element That Scans the Day of Week Element for Acceptable Values

<xs:schema
  xmlns:xs=“http://www.w3.org/2001/XMLSchema
  targetNamespace=“http://www.dustyscellar.com/TuscanLuncheon
  xmlns=“http://www.dustyscellar.com/TuscanLuncheon
  elementFormDefault=“qualified”>

  <xs:element name=“Dustys”>
    <xs:complexType>
      <xs:sequence>
        <xs:element ref=“Luncheon”
                   minOccurs=“0” maxOccurs=“unbounded”/>
     </xs:sequence>
   </xs:complexType>
 </xs:element>
<xs:element name=”Luncheon”>
  <xs:complexType>
    <xs:sequence>
      <xs:element name=”LuncheonId” type=”xs:string”/>
      <xs:element name=”Price” type=”xs:double”/>
      <xs:element name=”LimitedByDays”>
        <xs:complexType>
          <xs:sequence minOccurs=”0” maxOccurs=”unbounded”>
            <xs:element name=”LimitedByDayOfWeek” type=”acceptable-weekdays”/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>

      <xs:element ref=”Item”
                  minOccurs=”0” maxOccurs=”unbounded”/>
    </xs:sequence>
  </xs:complexType>
</xs:element>
<xs:simpleType name=”acceptable-weekdays”>
  <xs:restriction base=”xs:string”>
    <xs:pattern value=”(Sunday¦Monday¦Tuesday¦Wednesday¦Thursday¦Friday¦
→ Saturday)”/>
  </xs:restriction>
</xs:simpleType>

<xs:element name=”Item”>
  <xs:complexType>
    <xs:sequence>
      <xs:element name=”Name” type=”xs:string”/>
      <xs:element name=”Description” type=”xs:string”/>
    </xs:sequence>
  </xs:complexType>
 </xs:element> </xs:schema>

Because the new schema in Listing 23.3 indicates that there can now be multiple days that a luncheon is limited to, you will need to change the XML document. Listing 23.4 contains a revised XML document that complies with the schema in Listing 23.3.

Listing 23.4 If You Use the XML Schema in Listing 23.3, Modify the XML Document to Conform to the List of Possible Weekdays

<?xml version=”1.0” encoding=”utf-8” ?>
<Dustys xmlns=”http://www.dustyscellar.com/TuscanLuncheon”>
  <Luncheon>
    <LuncheonId>L1</LuncheonId>
    <Price>12.00</Price>
    <LimitedByDays>
    </LimitedByDays>
    <Item>
      <Name>Chicken Caesar Salad</Name>
      <Description>Tossed in mixed greens with Key Lime Caesar dressing</Description>
    </Item>
    <Item>
      <Name>Salmon Salad</Name>
      <Description>With assorted vegetables and Balsamic Vinaigrette</Description>
    </Item>
    <Item>
      <Name>Torta Rustica</Name>
      <Description> Our signature dish made with brioche
→ dough filled with smoked turkey, red peppers, provolone,
→ Gournay cheese and artichoke hearts</Description>
    </Item>
  </Luncheon>
  <Luncheon>
    <LuncheonId>L2</LuncheonId>
    <Price>21.00</Price>
    <LimitedByDays></LimitedByDays>
    <Item>
      <Name>Chicken Breast</Name>
      <Description>Stuffed with herb garlic cheese,
→ sun dried tomatoes and spinach</Description>
    </Item>
    <Item>
      <Name>Grilled 8oz Sirloin Steak</Name>
      <Description>With Pinoit Noir demi glaze</Description>
    </Item>
    <Item>
      <Name>Spinach Linguine</Name>
      <Description>With Tarragon Marsala cream, bacon,
→ wild mushrooms, shallots and Romano cheese</Description>
    </Item>
  </Luncheon>
  <Luncheon>
    <LuncheonId>F</LuncheonId>
    <Price>12.00</Price>
    <LimitedByDays>
      <LimitedByDayOfWeek>Saturday</LimitedByDayOfWeek>
      <LimitedByDayOfWeek>Sunday</LimitedByDayOfWeek>
    </LimitedByDays>
    <Item>
      <Name>French Toast</Name>
      <Description>Stuffed with seasonal berries</Description>
    </Item>
    <Item>
      <Name>Scrambled Eggs</Name>
      <Description>With bacon or sausage</Description>
    </Item>
    <Item>
      <Name>Spinach Crepe</Name>
      <Description></Description>
    </Item>
    <Item>
      <Name>Torta Rustica</Name>
      <Description> Our signature dish made with brioche
→ dough filled with smoked turkey, red peppers, provolone,
→ Gournay cheese and artichoke hearts</Description>
    </Item>
  </Luncheon>
</Dustys>

Notice that Listing 23.4 now has a LimitedByDays element and this element contains the child elements LimitedByDayOfWeek.

After you have chosen the form of your XML and schema documents, you set the BuildAction of the schema document to LinqToXsdSchema and compile the solution. To set the build action, complete the following steps:

1. Open the Solution Explorer.

2. Right-click the XSD document and select Properties.

3. Change the schema document’s Build Action to LinqToXsdSchema (see Figure 23.2).

4. Compile the solution.

Figure 23.2 Remember to set the BuildAction property of the schema document to LinqToXsdSchema.

Image

When you build the solution, the LinqToXsdSchema tool generates the XML wrapper classes, including the namespace. Figure 23.3 shows the Object Browser (F2) with the generated elements for the original XML and schema documents (from Listing 23.1 and 23.2).

Figure 23.3 The generated elements from the Object Browser for the XML and schema from Listings 23.1 and 23.2.

Image

Querying with LINQ to XML for Objects

After you have finished the modest amount of preparation work, you can write queries against the XML document using all of the features in regular LINQ to Objects. IntelliSense works (see Figure 23.4) as does the features discussed in Part II of this book.

Listing 23.5 demonstrates a query based on the XML and schema in Listings 23.1 and 23.2, and Listing 23.6 shows a revised query using the XML and schema from Listings 23.3 and 23.4, incorporating the regular expression element.

Listing 23.5 A LINQ Query That Hits the Wrapper Classes and XML Document for the XML and Schema from Listings 23.1 and 23.2

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using www.dustyscellar.com.TuscanLuncheon;

namespace DustysTuscanLuncheonMenu
{
  class Program
  {
    static void Main(string[] args)
    {
      var dustys = Dustys.Load(“../../Menu.xml”);
      var menu = from lunchMenu in dustys.Luncheon
                 from item in lunchMenu.Item
                 let cost = lunchMenu.Price * 8 * 1.06
                 where lunchMenu.LimitedByDayOfWeek == “Sunday”
                 select new {MenuItem=item.Name,
                   Description=item.Description,
                   Cost=string.Format(“{0:C}”, cost)};

      Array.ForEach(menu.ToArray(), m=>Console.WriteLine(m));
      Console.ReadLine();

    }
  }
}

Figure 23.4 IntelliSense picks up the namespaces, classes, and members generated in the wrapper class for LINQ to XSD (also called LINQ to XML for Objects).

Image

Listing 23.6 A Sample Program That Uses the Revised Schema and XML for Listings 23.3 and 23.4, Incorporating the Collection of Luncheon Days and the Regular Expression

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using www.dustyscellar.com.TuscanLuncheon;

namespace DustysTuscanLuncheonMenu
{
  class Program
  {
    static void Main(string[] args)
    {
      var dustys = Dustys.Load(“../../Menu.xml”);
      var menu = from lunchMenu in dustys.Luncheon
                 from item in lunchMenu.Item
                 let cost = lunchMenu.Price * 8 * 1.06
                 let days = lunchMenu.LimitedByDays
                 where days.LimitedByDayOfWeek.Contains(“Sunday”)
                 select new {MenuItem=item.Name,
                   Description=item.Description,
                   Cost=string.Format(“{0:C}”, cost)};

      Array.ForEach(menu.ToArray(), m=>Console.WriteLine(m));
      Console.ReadLine();

    }
  }
}

If you are curious, the LinqToXsd tool generates classes containing a default constructor, properties for children, Load, Save, and Clone members, and an explicit cast operator for the XElement type. You can view this information in the Object Browser or a tool like Reflector.

LINQ to XSD is still in its incubation phase. This means that some of the details here might not work in future versions. However, the basic design goals are to support a more natural interaction with XML documents and maintain a higher level of fidelity between the intent of the schema—like supporting restrictions and regular expressions—between the XML Schema and the code-generated wrapper classes.

Summary

The way the .NET Framework is layering complexity into the framework on top of cool technologies like the CodeDOM and LINQ to XML (with LINQ to XSD) is inspired genius. This takes some of the complexity out of programming and using technologies like XML and XSD for us beleaguered programmers, letting us focus on solving problems rather than wrangling with technologies.

Check out the weblogs for more on LINQ to XSD. Everything in this book except LINQ to Entities (Chapter 17, “Introducing ADO.NET 3.0 and the Entity Framework”) and LINQ to XSD is ready for prime time. Enjoy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.173.53