We have already seen that many of the LINQ to XML methods navigate XML content and return instances of type IEnumerable<XNode> or IEnumerable<XElement>. Because LINQ queries can be applied to sequences of IEnumerable<T>, we can also use them to query XML content. In some of the previous examples, we used LINQ queries to create new XML content or to transform XML graphs.
Now consider a different situation: you want to create sequences of items (objects, entities, or whatever) whose value is taken from XML content. We can use LINQ queries over XML nodes through LINQ to XML syntax.
Listing 6-32 is an example of a LINQ query applied to the well-known XML list of customers. It filters the list of customer elements to extract the name element and the city attribute of each customer located in Italy, ordering the result by the name element value.
Listing 6-32: Using LINQ to XML and LINQ queries to query XML content
var customersFromXml = from c in xmlCustomers.Elements("customer") where (String)c.Attribute("country") == "Italy" orderby (String)c.Element("name") select new { Name = (String)c.Element("name"), City = (String)c.Attribute("city") }; foreach (var customer in customersFromXml) { Console.WriteLine(customer); }
The result is shown in the following output block:
{ Name = Marco, City = Torino } { Name = Paolo, City = Brescia }
This result is interesting, even if it is not all that exciting. To make these opportunities more challenging, suppose you have the same XML list of customers and a sequence of orders defined using the following LINQ query:
var orders = from c in customers from o in c.Orders select new {c.Name, o.IdProduct, o.Quantity};
You can imagine that the orders were loaded via LINQ to Entities from a Microsoft SQL Server database. We can write a complex query that joins XML nodes with entities to extract a sequence of new objects, as shown in Listing 6-33.
Listing 6-33: A LINQ query that merges LINQ to XML and LINQ to Objects
var ordersWithCustomersFromXml = from c in xmlCustomers.Elements("customer") join o in orders on (String)c.Element("name") equals o.Name orderby (String)c.Element("name") select new { Name = (String)c.Element("name"), City = (String)c.Attribute("city"), IdProduct = o.IdProduct, Quantity = o.Quantity };
This is a new and really powerful feature of LINQ and LINQ to XML, because we can define queries over mixed contents using a unique language and programming environment.
If you do not like the repetition of explicit casting of XML nodes inside the LINQ query, remember that we can use the let clause to define a more maintainable alias, as shown in Listing 6-34.
Listing 6-34: A LINQ query that merges LINQ to XML and LINQ to Objects, simplified by using the let clause
var ordersWithCustomersFromXml = from c in xmlCustomers.Elements("customer") let xName = (String)c.Element("name") let xCity = (String)c.Attribute("city") join o in orders on xName equals o.Name orderby xName select new { Name = xName, City = xCity, IdProduct = o.IdProduct, Quantity = o.Quantity };
Both of the previous examples return a sequence that, when printed to the Console window, looks like the following:
{ Name = Frank, City = Seattle, IdProduct = 5, Quantity = 20 } { Name = James, City = Dallas, IdProduct = 3, Quantity = 20 } { Name = Marco, City = Torino, IdProduct = 1, Quantity = 10 } { Name = Marco, City = Torino, IdProduct = 3, Quantity = 20 } { Name = Paolo, City = Brescia, IdProduct = 1, Quantity = 3 } { Name = Paolo, City = Brescia, IdProduct = 2, Quantity = 5 }
Using a LINQ query, we can also create a new XML graph, merging XML nodes and entities.