In This Chapter
Understanding the Basic Design Goals of LINQ to XSD
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.
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.
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.
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.)
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.
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.
<?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.
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.
<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
.
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).
<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.
<?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.
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).
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.
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();
}
}
}
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.
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.
3.128.173.53