Chapter 2. Getting Started

Many developers prefer to use a new technology rather than simply read about it. Practical experience provides a foundation on which to construct the theoretical understanding needed when mastering a new skill.

This chapter helps you understand LINQ by showing several simple programs that illustrate

• LINQ to Objects

• LINQ to SQL

• LINQ to XML

These examples demonstrate three themes that recur frequently in this book:

• The usefulness of query expressions

• The significance of deferred execution

• The primacy of IEnumerable<T>

The examples shown in this chapter also illustrate how to write query expressions, the key syntactic construct used by LINQ developers to query a data source. When executing even these simple LINQ queries, you will encounter deferred execution, a characteristic of LINQ that developers must comprehend if they want to claim a thorough knowledge of the subject. Finally, you will be introduced to IEnumerable<T>, the data source for LINQ to Objects and LINQ to XML queries. These queries usually also return a variable of this type. A thorough understanding of LINQ is impossible without first becoming acquainted with IEnumerable<T>.

This chapter also introduces several new features of C# 3.0 that are not LINQ-specific:

• Type inference

• Collection initializers

• Object initializers

• Automatic properties

These features are discussed in more depth in Chapter 4, “C# 3.0 Technical Overview.” That chapter also covers other important features, such as lambdas and extension methods.

Querying a Collection of Integers

Our first query will be run against a collection of integers. Listing 2.1 shows a complete program demonstrating how to write a LINQ query against a collection that contains the numbers 1, 2, and 3. The query selects the numbers in the collection that are smaller than 3 and prints them to the screen.

Listing 2.1. When Compiled, the Source for the SimpleNumericQuery Program Returns the Values 1 and 2

using System;
using System.Collections.Generic;
using System.Linq;

namespace NumericQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> list = new List<int>() { 1, 2, 3 };


            var query = from number in list
                        where number < 3
                        select number;

            foreach (var number in query)
            {
                Console.WriteLine(number);
            }

        }
    }
}

There are two simple ways to compile and run this program:

• Method 1:

1. Enter the program directly into a default console application in Visual Studio 2008 or later.

2. Press F5 to run it. (If you press Ctrl-F5, it will run and the console window will stay open so that you can view the results. Alternatively, you could add a Console.Readline() statement to the end of the listing.)

• Method 2:

1. Open a text editor and enter Listing 2.1.

2. Save the text file as SimpleNumericQuery.cs.

3. Compile and run the program by entering the following at the command prompt:

PATH=%PATH%;%windir%Microsoft.NETFrameworkv3.5
csc.exe SimpleNumericQuery.cs
SimpleNumericQuery.exe

The first line sets the path to give you access to the .NET Framework. The second line compiles the program. The third line executes it. When run, the program’s output displays the numbers 1 and 2. I should add that two assemblies, System and System.Core, are implicitly included in your application when you compile it. Appendix A contains more information on compiling and running C# programs.

Collection Initializers

The first line of code in the body of the SimpleNumericQuery program uses a new feature of C# 3.0 called collection initializers. This feature helps you populate a collection using a concise and easy-to-read syntax.

Consider this single line of code that initializes a collection with three integers:

List<int> list = new List<int>() { 1, 2, 3 };

This single line of code is called a collection initializer. It is a shorthand way of writing the following code:

List<int> list = new List<int>();
                 list.Add(1);
                 list.Add(2);
                 list.Add(3);

Although collection initializers are not part of LINQ proper, they are written in the spirit of LINQ in that they allow you to concisely declare your intentions in code that is easy to understand.

Query Expressions

The centerpiece of Listing 2.1 resides in three lines of code called a LINQ query expression:

var query = from number in list
            where number < 3
            select number;

Query expressions will be analyzed in more depth in later chapters; for now we will only take a quick look at their most salient features.

On the right side of the = operator, you see the body of the query:

from number in list
where number < 3
select number;

All query expressions begin with the keyword from and end with a line that begins with the select or group by contextual keywords. It is important that you fully understand these keywords or the query operators that underlie them. It is also important to know that query expressions always begin with a from clause and usually end with a select clause.

The where clause in the second line of the query expression shown in Listing 2.1 instructs the compiler to filter the numbers in the list, returning only those that are smaller than 3. Chapter 6, “Query Operators,” describes 49 different operators, such as where and select, that are available in LINQ to Objects. However, the pattern shown here, with a from, where, and select clause, is the most commonly used.


Contextual Keywords

Contextual keywords are not reserved words in any traditional sense. They are words that have a significant meaning only when used in a particular setting. For instance, the words from, where, and select, when used in a query expression with the pattern just shown, have specific and important significance. Some contextual keywords may have more than one meaning, depending on their context. For instance, where can also be used as a contextual keyword to define a generic constraint. The following are the contextual keywords used in C# 3.0:

• LINQ contextual keywords found in query expressions: from, where, join, on, equals, into, let, orderby, ascending, descending, select, group, and by

• Property-based contextual keywords: get, set, value

• Other contextual keywords: partial, var, yield


The select clause in a query expression comes on the last line. This might seem counterintuitive if you’re not used to SQL queries. Here is why the select clause appears on the last line of a LINQ query:

• Query expressions in LINQ are fully type-checked and IntelliSense-aware.

• If the select clause came first, the IDE and the compiler would not immediately know the type of data you wanted to query. As a result, they could not provide type checking or IntelliSense while you were composing your query.

If you place the from clause first, the compiler is immediately informed of the type of data you want to query, and it can begin giving you feedback as you type. In strongly typed languages such as C#, you always establish the type as quickly as possible, and LINQ simply follows that well-established pattern by beginning query expressions with a from clause.

Although it may seem strange at first, I’ve found that the logic that led the team to begin query expressions with a from clause is so compelling that it quickly became second nature to me. I’ve been told that in Microsoft SQL Server, the from clause of a T-SQL query is actually executed first, and then the joins, and then the where clause. The select clause is actually the last part of the query to be evaluated.

Type Inference

On the left of the = operator, you see the words var query. The new contextual keyword var tells the compiler to rely on type inference to infer the type of the identifier query. The type is determined by an analysis of the expression on the right of the operator.


Quick Insight into Type Inference

In Visual Studio, if you hover the mouse over the word var, a window appears showing its underlying type.


This query, like most LINQ to Objects queries, returns a variable of type IEnumerable<T>. In this case, T is of type int. Therefore, you could have declared the query expression as follows:

IEnumerable<int> query = from number in list
                         where number < 3
                         select number;

Although this code is valid, the preferred style is to use the contextual keyword var. Type inference provides several benefits. It ensures that strong typing is enforced, and it also

Eliminates the need to guess the type of the data returned from a LINQ query.

• Eliminates verbose and repetitive code in some circumstances.

• Makes possible the use of a new feature of C# 3.0 called anonymous types when you’re writing query expressions.

• Allows you to easily use a powerful feature of LINQ called composability.

All these features of type inference are discussed in more depth later in this book. Anonymous types are discussed in this chapter, and composability in the next.

Our sample uses a foreach loop to iterate over the results of the query. As mentioned, this loop prints the numbers 1 and 2.

Introduction to IEnumerable<T>

A foreach loop can iterate over the results of this query because it is of type IEnumerable<T>. Objects that implement IEnumerable<T> have access to the methods and properties MoveNext(), Current, and Reset(). They can all be enumerated like this:

List<int>.Enumerator e = list.GetEnumerator();

while (e.MoveNext())
{
   Console.WriteLine(e.Current);
}

A foreach loop is simply the preferred shorthand way of writing the preceding code:

foreach (var number in query)
{
  Console.WriteLine(number);
}

The foreach loop is preferable because it expands into a try/catch/finally construct that calls the Dispose method of IEnumerable<T>. The example shown here does not.

Let’s take a moment to review and emphasize the central role of IEnumerable<T> in LINQ to Objects. Here are the key points:

• LINQ to Objects queries are run against variables that support the IEnumerable<T> interface.

• They also usually return a variable of type IEnumerable<T>. To see the return type, hover the mouse over the word var, as described earlier in this chapter.

This simple program has introduced four key concepts:

• Collection initializers

• Type inference

• Query expressions

IEnumerable<T>

All these technologies are important, but the latter three are central, recurring themes of this text.

Querying a Collection of Objects

The previous example showed how to query a collection of the simple Integer type. This next example demonstrates how to query a collection of objects. The objects are of a custom type called Customer:

class Customer
{
  public string CustomerID { get; set; }
  public string ContactName { get; set; }
  public string City { get; set; }
}

The declaration for this class uses a new C# 3.0 feature called automatic properties that is designed to help you easily declare properties in a concise style.

Introducing Automatic Properties

The original C# syntax for properties involved using get and set methods to access data. For instance, the City property shown previously would look like this in C# 2.0:

private string city;

public string City
{
   get
   {
      return city;
   }

   set
   {
      city = value;
   }
}

This syntax still works in C# 3.0, but now you can also use this simple shorthand to produce semantically equivalent code.

The code produced when you use automatic properties includes an inaccessible private backing store. One way to see this is to use the free program available on the Internet called Red Gate’s .NET Reflector. If you right-click the Customer class and choose Disassemble, that program produces the following code:

internal class Customer
{
    // Fields
    [CompilerGenerated]
    private string <City>k__BackingField;
    [CompilerGenerated]
    private string <ContactName>k__BackingField;
    [CompilerGenerated]
    private string <CustomerID>k__BackingField;

    // Methods
    public Customer();


    // Properties
    public string City { [CompilerGenerated] get; [CompilerGenerated]
    set; }
    public string ContactName { [CompilerGenerated] get;
        [CompilerGenerated] set; }
    public string CustomerID { [CompilerGenerated] get;
        [CompilerGenerated] set; }
}

Three backing fields are declared with oddly shaped identifiers such as <CustomerID>k__BackingField. They would never compile in C#, although they are valid Intermediate Language (IL) identifiers. Code similar to what is shown here is generated for you in the background whenever you use automatic properties.


Intermediate Language (IL)

The C# compiler translates the code that we write into Intermediate Language (IL). IL is executed at runtime by the .NET Common Language Runtime (CLR).


If you want to access the backing fields of a property, you must declare them using the traditional property style. Automatic properties are useful only as a means of doing less typing and keeping your code short and precise.

Introducing Object Initializers

In the preceding section, you saw how to initialize a collection of integers in C# 3.0. Here is how to use similar syntax to initialize a collection of objects:

private static List<Customer> GetCustomers()
{
  return new List<Customer>
  {
    new Customer { CustomerID = "ALFKI", ContactName = "Maria Anders",
        City = "Berlin" },
    new Customer { CustomerID = "ANATR", ContactName = "Ana Trujillo",

        City = "Mexico D.F." },
    new Customer { CustomerID = "ANTON", ContactName = "Antonio Moreno",
        City = "Mexico D.F." }
  };
}

The lines beginning with the word new are examples of object initializers. They instantiate an instance of the object Customer and initialize all three of its public properties. You can also initialize fields with this same syntax.

Like collection initializers, object initializers are a shorthand way of performing a common task. In particular, the first new statement in the GetCustomers method looks like this in C# 2.0 syntax:

Customer customer1 = new Customer();

customer1.CustomerID = "ALFKI";
customer1.City = "Berlin";
customer1.ContactName = "Maria Anders";

This code still compiles, but the new syntax is clearly more concise.

The entire body of the GetCustomers method is a collection initializer. This time, instead of initializing a collection of integers, three objects are placed in the collection. I won’t waste space in this text showing how much code it would take to perform the same task using C# 2.0 syntax. It should be obvious that the new syntax is both shorter and easier to read.

The code in Listing 2.2 is a complete program demonstrating how to use LINQ to Objects to query a collection of Customer objects.

Listing 2.2. A Simple LINQ to Objects Query Against a Collection of Customer Objects

using System;
using System.Collections.Generic;
using System.Linq;

namespace SimpleLinqToObjects
{
  class Customer
  {
     public string CustomerID { getset; }
     public string ContactName { getset; }
     public string City { getset; }
  }

  class Program
  {

    private static List<Customer> GetCustomers()
    {
      return new List<Customer>
      {
        new Customer { ContactName = "Maria Anders", City = "Berlin" },
        new Customer { ContactName = "Ana Trujillo", City =
                       "Mexico D.F." },
        new Customer { ContactName="Antonio Moreno", City="Mexico D.F." }
      };
    }

    static void Main(string[] args)
    {
      var query = from c in GetCustomers()
                  where c.City == "Mexico D.F."
                  select new { City = c.City, ContactName =
                               c.ContactName };

      foreach (var cityAndContact in query)
      {
         Console.WriteLine(cityAndContact);
      }
    }
  }
}

As in the previous example, the query expression in this program is three lines long:

var query = from c in GetCustomers()
            where c.City == "Mexico D.F."
            select new { City = c.City, ContactName = c.ContactName };

It takes each of the three customers and filters out those in which the City field is not set to Mexico D.F.

Introducing Anonymous Types

Notice the last line of the preceding query:

select new { City = c.City, ContactName = c.ContactName };

This line creates an anonymous type. Behind the scenes, at compile time, a very simple class is generated automatically. In this particular case, two properties, City and ContactName, are added to the class.

In this case the names of these properties are explicitly called out:

City = c.City, ContactName = c.ContactName

However, you could allow the compiler to derive the names from the fields themselves:

select new { c.City, c.ContactName };

This code would again create two properties called City and ContactName. In many cases, you can use either syntax, depending on your preference. In some cases you might choose to change the name of one or more fields:

select new { Town = c.City, Contact = c.ContactName };

In later chapters, you will see cases in which the compiler forces you to create names to distinguish fields from two objects that have the same name.

The foreach loop at the end of the program implicitly calls the automatically implemented ToString() method for this anonymous object to format the program’s output:

{ City = Mexico D.F., ContactName = Ana Trujillo }
{ City = Mexico D.F., ContactName = Antonio Moreno }

Here you see output based on the two fields of our very simple anonymous class.

The example shown in this section demonstrated how to write a simple LINQ query that retrieves data from a collection of objects. You might still have questions about the three technologies introduced here:

• Object initializers

• Automatic properties

• Anonymous types

These subjects are covered in more depth in Chapter 4.

A Simple LINQ to SQL Example

Listing 2.3 illustrates the technology on which LINQ to SQL is built. To keep this example as concise as possible, much of the machinery that makes this technology powerful and flexible has been stripped away. All that is left is the minimum code required to query a database with LINQ to SQL.

Listing 2.3. The LinqToSqlWithoutDesigner Sample Demonstrates How to Use LINQ to SQL to Query a Database

using System;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;

namespace LinqToSqlWithoutDesigner
{

    [Table(Name = "Customers")]
    class Customer
    {
        [Column]
        public string CustomerID;
        [Column]
        public string City;
    }

    class Program
    {
        static void Main(string[] args)
        {

            DataContext db = new DataContext(@"c:data orthwnd.mdf");

            var query = from c in db.GetTable<Customer>()
                        where c.City == "London"
                        select new { CustId = c.CustomerID, City =
                                     c.City };

            foreach (var cust in query)
            {
                Console.WriteLine(cust);
            }
        }
    }
}

This code assumes the presence of SQL Server Express on your development system. You also need a copy of the Northwind database. It is available as a free download over the web. It also ships with the official C# samples found in the MSDN Code Gallery. See Appendix A for additional information on obtaining and setting up the Northwind database. In this example, I have stored the database in a directory on the C drive called Data. You can change the path if you want to, but you must have a copy of the database to run this sample. If you meet these prerequisites, you should be able to compile and run the program using the same commands you used in the previous examples. If you need help meeting these requirements, or if you are having trouble connecting to the database, see Appendix A.


User Instances Enabled

To compile this program, you need to include a reference to System.Data.Linq.dll. If you are working in Visual Studio, bring up the Solution Explorer, open the References node, and right-click to add this assembly from the .NET page.

To get this sample to run correctly, you may also have to run these commands in a query window in SQL Server Management Studio Express:

exec sp_configure 'user instances enabled', 1.
Reconfigure


The preceding code has two interesting sections. The first is the declaration of the class called Customer. In LINQ, classes like this are called entities.

Entity classes are designed to map directly to a table in a database. The compiler knows to perform this mapping because of the Table attribute above the declaration of the class:

[Table(Name = "Customers")]
class Customer

This simple attribute tells the LINQ runtime that this class is designed to mirror a table in the database. As soon as LINQ knows to link the table to the class, it can automatically populate instances of the class with the data from the database.

Before LINQ can correctly map the Customer table to the Customer class, it must know how the fields in the database table map to the fields in the C# class. The two Column attributes shown in the declaration of the Customer class map the properties of the class in your program to the fields of the table in the database:

[Column]
public string CustomerID;

LINQ uses this information when it maps data pulled from the database to instances of the Customer class.

Let’s now consider the initialization of the DataContext. This class performs several tasks for developers including:

• It automatically sets up a connection to the database.

• It maps the rows of data retrieved from the database to instances of the Customer class.

To set up the connection, we only need to pass the location of the database that we want to query to one of the DataContext’s constructors:

DataContext db = new DataContext(@"c:data orthwnd.mdf");

The DataContext also plays a role in the query expression run against the data in the database:

var query = from c in db.GetTable<Customer>()
            where c.City == "Mexico D.F."
            select new { CustId = c.CustomerID, City = c.City };

The query expression shown here looks very much like those that we wrote in the previous LINQ to Objects example. The only difference between the two queries is in the last part of the from clause:

var query = from c in GetCustomers()            // LINQ to Objects
            where c.City == "Mexico D.F."
            select new { City = c.City, ContactName = c.ContactName };

var query = from c in db.GetTable<Customer>()   // LINQ to SQL
            where c.City == "Mexico D.F."
            select new { City = c.City, ContactName = c.ContactName };

Other than the from clause, the entire query expression—including the where clause, the select clause, and the anonymous type—is identical. This fact is emphasized again in the next chapter, which discusses the unitive principle of LINQ development.

Despite the similar syntax, the LINQ to SQL example is, in fact, very different from the LINQ to Objects example. In LINQ to Objects, the data is pulled from a collection in your program. In LINQ to SQL, the entire query expression is converted into a SQL statement, and the statement is executed against a database that resides in a different process. Finally, the data returned from the query is ferried between processes and is converted into instances of the Customer object.

One of this book’s primary goals is to explain exactly how LINQ to SQL works. To fully understand that subject, you need to study lambdas, expression trees, and extension methods. You also need to understand how IEnumerable<T> and IQueryable<T> are implemented, and why they were implemented. All of that lies before us.

This chapter’s purpose, however, is simply to give you a working example of LINQ to SQL and to point out some of its most salient features. If you now also find yourself anticipating the unveiling of some of the secrets behind this fascinating technology, all the better.

I’d like to close this section by showing you a simple program that I do not expect you to compile. In Listing 2.3, you saw the declaration for the Customer entity class and learned how it was mapped to a class in the database. The average database in a line-of-business application might have more than 100 tables, and many of those tables might have as many as 20 or more fields. Clearly, it would be a major undertaking to map each of those tables to handmade C# classes.

Fortunately, LINQ ships with Object Relational Mapping (ORM) tools that automatically create classes that map to the tables in your database. These tools relieve you of the need to manage the entity classes in your program. As a result, you will be able to write programs like the one shown in Listing 2.4. This program, has no declaration for the Customer entity class. Instead, it was created behind the scenes by the ORM tools that ship with Visual Studio or the .NET Framework 3.5. All of this will be described in some depth in Chapters 7 through 10.

Listing 2.4. A Simple Example of Using LINQ to SQL to Query a Database After Running the Object Relational Designer

using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace GettingStartedWithLinqToSql
{
    class Program
    {
        static void Main(string[] args)
        {
            Northwind db = new Northwind(@"C:DataNorthwnd.mdf");

            var query = from c in db.Customers
                        where c.City == "Nantes"
                        select new { c.City, c.CompanyName };

            foreach (var q in query)
            {
                Console.WriteLine(q);
            }
        }
    }
}

LINQ to XML

LINQ to XML makes it easy for you to create, parse, and transform XML files. In this section you see two programs. The first, shown in Listings 2.5 and 2.6, reads in a simple XML file and runs a query against it. The second, shown in Listing 2.7, demonstrates how to create the XML file used in the first program.

Parsing XML

Let’s begin by studying the code shown in Listing 2.5. This program reads in a simple XML file and queries the data in the file to retrieve only the rows where the City attribute is set to Mexico D.F. To run this program, you need to reference the System.Xml.Linq.dll assembly, which is included by default when you create a console application in Visual Studio.

Listing 2.5. A Simple LINQ to XML Program That Parses a Small XML File and Finds Customers Who Live in Mexico D.F.

using System;
using System.Linq;
using System.Xml.Linq;

namespace SimpleXmlCustomers
{
    class Program
    {
        static void Main(string[] args)
        {
            XDocument customers = XDocument.Load(@"Customers.xml");

            var xml = from x in customers.Descendants("Customer")
                      where x.Attribute("City").Value == "Mexico D.F."
                      select x;

            foreach (var x in xml)
            {
                Console.WriteLine(x);
            }
        }
    }
}

Listing 2.6. The Data Stored in the Customers.xml File Used in the SimpleXmlCustomers Program

<?xml version="1.0" encoding="utf-8" ?>
<Customers>
 <Customer ContactName="Maria Anders" City="Berlin" />
 <Customer ContactName="Ana Trujillo" City="Mexico D.F." />
 <Customer ContactName="Antonio Moreno" City="Mexico D.F." />
</Customers>

In Listing 2.5, notice the using statement that introduces the System.Xml.Linq namespace. All the key types you will use in LINQ to XML are found in this namespace. Each one is discussed in depth in Chapter 13, “LINQ to XML: Creation.”

Listing 2.6 contains a short XML file that you can easily type in by hand. If you download the samples, you can find longer copies of this file that you can use when composing more complex queries.

The code for loading this XML file is very simple:

XDocument customers = XDocument.Load(@"Customers.xml");

The XDocument class is declared in the System.Xml.Linq namespace. The static Load method of this class is used to transfer the XML file from disk into memory. After its execution, the customers object contains a fully parsed XML file.

The following code shows how to write a query against the XML that has been loaded into memory:

var xml = from x in customers.Descendants("Customer")
          where x.Attribute("City").Value == "Mexico D.F."
          select x;

This query looks very much like the LINQ to SQL query, which, in turn, looked much like the LINQ to Objects query. The overall pattern of the three queries is essentially identical:

from x in XXX
where x == YYY
select x

In this case, however, the details are quite different from what you saw in the LINQ to Objects and LINQ to SQL examples. Why is this?

• Both CSharp collections and SQL databases contain discrete types and rigid structures that are easily mapped to C# objects. Although XML schemas can help you pin down the types in an XML file, currently there is no way to directly map a C# object to a row in an XML file.

The “rows” of data in an XML file can contain elements, attributes, comments, or other XML types such as CDATA. As a result, we need to have classes with names such as XElement, XAttribute, and XComment to work with these different types. There was no inherent need for this kind of complexity when working with LINQ to SQL or LINQ to Objects.

For both of these reasons, LINQ to XML queries differ substantially from the queries shown earlier in this chapter. An entire lengthy section of this book is dedicated to explaining how LINQ to XML works. In this chapter, however, I’ll just say a few simple words to help walk you through the most obvious sections of our LINQ to XML sample.

Take a look at the from clause in the query expression. The Descendants operator of the XDocument class locates all the descendants of the XML document, starting at a specified depth. In particular, the code is designed to search only the “Customer” elements in our document:

from x in customers.Descendants("Customer")

Here are those nodes as they appear in the XML document itself:

<Customer ContactName="Maria Anders" City="Berlin" />
<Customer ContactName="Ana Trujillo" City="Mexico D.F." />
<Customer ContactName="Antonio Moreno" City="Mexico D.F." />

As you can see, these customer nodes correspond, roughly, to rows in a database.

When iterating over the data at runtime, the range variable x in the from clause of this query contains a single XML customer node and its ContactName and City attributes:

<Customer ContactName="Maria Anders" City="Berlin" />

A LINQ to XML type called XElement is used to store this data. Inside each XElement you will find the attributes called ContactName and City. This data is stored in instances of a type called XAttribute. To get started using LINQ to XML, you only need to know that these types are wrappers around their respective node types in the XML file. For instance, in this case an XElement wraps a single Customer node, and an XAttribute type wraps the ContactName and City attributes of that node.

A filter also is specified in our query:

where x.Attribute("City").Value == "Mexico D.F."

The where operator is used here to filter out all the Customer nodes that do not have their city set to Mexico D.F. The Attribute method of the range variable x returns a variable of type XAttribute.

The final line in the query projects the result returned to the user. In this case, we are simply selecting the Customer nodes that have their City attribute set to Mexico D.F. As a result, the program prints the following:

<Customer CustomerID="ANATR" ContactName="Ana Trujillo" City=
  "Mexico D.F." />
<Customer CustomerID="ANTON" ContactName="Antonio More" City=
  "Mexico D.F." />

Although there is no need to do so in this case, you could create an anonymous object in the select statement:

select new { ContactName = x.Attribute("ContactName").Value,
   City = x.Attribute("City").Value } ;

Creating XML

Now that you have learned a little about querying an XML file, the next step is to learn how to create an XML file. Listing 2.7 shows the code for building the XML shown in Listing 2.6.

Listing 2.7. Creating the Simple XML File Shown in Listing 2.6

using System;
using System.Xml.Linq;

namespace CreateXmlCustomers
{
    class Program
    {
        static void Main(string[] args)
        {

            XDocument doc = new XDocument(new XDeclaration("1.0",
                                          "utf-8""yes"),
                new XElement("Customers",
                    new XElement("Customer",
                        new XAttribute("ContactName""Maria Anders"),
                        new XAttribute("City""Berlin")),
                    new XElement("Customer",
                        new XAttribute("ContactName""Ana Trujillo"),
                        new XAttribute("City""Mexico D.F.")),
                    new XElement("Customer",
                        new XAttribute("ContactName""Antonio Moreno"),
                        new XAttribute("City""Mexico D.F."))
            ));

            Console.WriteLine(doc.Declaration);
            Console.WriteLine(doc);
            doc.Save(@"Customers.xml");
        }
    }
}

The document is created in a single statement that is written in the functional style. Many other XML tools have you create the various attributes and elements one at a time and then add them to the document individually. LINQ to XML also supports that syntax, but the preferred technique is to create a single statement, as shown here, that reflects the document’s structure.

This program uses four types, two of which you saw in the previous example. For now, I will be content to describe the roles they play in this example:

XDocument: A wrapper for an XML tree that contains XElements, XAttributes, XDeclarations, and other types such as XComment.

XDeclaration: An optional type used to specify the XML version, the encoding, and whether the XML document is stand-alone.

XElement: This type represents an XML element. Like an XDocument, it can be used to construct XML trees. It typically contains a name and some content. It can contain other types such as XAttributes and XComments.

XAttribute: Represents an XML attribute. Each XAttribute contains a name-value pair.

If you wanted to create a single XElement, you could write code like this:

XElement xml = new XElement("Node""Content");

This code creates a valid XML element. When written to the console, the output of the variable xml would look like this:

<Node>Content</Node>

If you wanted to add an attribute to your node, you could write the following code:

xml = new XElement("ElementName",
   new XAttribute("AttributeName""value"));

When written to the console with a WriteLine statement, this code produces the following simple XML tree:

<ElementName AttributeName="value" />

If you wanted to use an XDocument, you could write the following code:

XDocument document = new XDocument(
   new XElement("ElementName",
      new XAttribute("AttributeName""value")));

This document, when written to the console, produces exactly the same output as the previous example. The XDocument type adds something of value to your code only when you want to use a type, such as XDeclaration, that is specific to the XDocument type.

Listing 2.7 shows an example of using the XDeclaration type with an XDocument. You should now be able to go back to Listing 2.7 and apply the principles shown in the sample examples we have been studying. You should be able to see how XDocument, XDeclaration, XElement, and XAttribute types were combined to create an XML document.

The simple examples in this subsection aim to initiate you into the world of LINQ to XML. Designed to pique your curiosity, they have almost certainly raised as many questions as they have answered. More in-depth coverage of this subject will be included in Chapter 13.

Summary

This chapter contained several fairly short programs designed to introduce you to the most common forms of LINQ programs: LINQ to Objects, LINQ to SQL, and LINQ to XML. All the programs were simple, and many important details were covered only in passing. This is an important chapter nevertheless, because it gave you an opportunity to work with real LINQ code. Technologies often become real to you only when you actually start to use them. This chapter gave you a chance to run real LINQ programs that you can easily create by hand in just a few minutes.

Several technologies were introduced that will be touched on repeatedly in this book. They include two LINQ technologies:

• Query expressions

• Deferred execution

and four new features of C# 3.0:

• Object initializers

• Collection initializers

• Automatic properties

• Type inference

You also learned that all the generic collections in C# support IEnumerable<T>. Thus, the List<int> collection used in the first query in this chapter supports that interface. If it did not, we could not use that type in a from clause:

from number in list

All LINQ to Objects queries follow this pattern:

from x in SomeVariableThatSupportsIEnumerable<T>

LINQ to Objects revolves around IEnumerable<T>. It is the alpha and omega of LINQ to Objects. Each query consumes this type, and they usually return it.

LINQ to SQL is the subject of several weighty chapters at the heart of this book. Using LINQ to write SQL queries is a very important subject, and one that will be explored in considerable depth. This chapter includes only the minimum code necessary to introduce you to the topic.

You also had a quick look at LINQ to XML. You saw that the XDocument class contains a method called Descendants that can be used to discover a particular node type in a document. When converted into LINQ to XML, these nodes typically are of type XElement, and they frequently contain subnodes of type XAttribute. In Chapter 13, you will learn more about these types and how to use them when you parse, transform, and create XML documents.

This chapter gave you just enough information to help you understand the basics of LINQ. In the next chapter, you will learn about the theoretical foundation on which LINQ is built. The practical knowledge in this chapter, and the theoretical knowledge found in the next chapter, will give you a strong foundation on which to begin an in-depth study of how to use LINQ to query data.

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

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