Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 792 Part V Data Connectivity LINQ to XML example Create a new Visual Studio project. On the form, add a text box and two buttons. Set the properties of the text box so that it is multi-line and has a vertical scroll bar. Behind button1, add the following code: XDocument riders = new XDocument (new XDeclaration("1.0", "utf-8", "yes"), new XComment("Riders for the year 2008"), new XElement("Riders", new XElement("Rider", new XAttribute("Residence", "Florida"), new XElement("Name", "Ricky Carmich ael"), new XElement("Class", "450"), new XElement("Brand", "Suzuki"), new XElement("Sponsors", new XElement("Name", "Makita") ) ), new XElement("Rider", new XAttribute("Residence", "California"), new XElement("Name", "Chad Reed"), new XElement("Class", "450"), new XElement("Brand", "Yamaha"), new XElement("Sponsors", new XElement("Name", "ProTaper") ) ), new XElement("Rider", new XAttribute("Residence", "Mississippi"), new XElement("Name", "Kevin Windham"), new XElement("Class", "450"), new XElement("Brand", "Honda"), new XElement("Sponsors", new XElement("Name", "Factory Connection") ) ), new XElement("Rider", new XAttribute("Residence", "Florida"), new XElement("Name", "James Stewart"), new XElement("Class", "450"), new XElement("Brand", "Kawasaki"), new XElement("Sponsors", new XElement("Name", "Renthal") ) ) ) ); textBox1.Text = riders.ToString(); 792 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 793 LINQ 34 Be sure to add the appropriate reference to System.XML.Linq in your form prior to running the project: using System.XML.Linq; Before running the project, take a look at the code. Notice that several of the classes were used to create and define the XML. For example, the XDocument class was used to create an XML document. As well, the XElement and XAttribute classes were used to define the attributes and elements of the XML document. Run the project and click button1 when the form displays. The text box on the form will display the following XML: <! Riders for the year 2008 > <Riders> <Rider Residence="Florida"> <Name>Ricky Carmichael</Name> <Class>450</Class> <Brand>Suzuki</Brand> <Sponsors> <Name>Makita</Name> </Sponsors> </Rider> <Rider Residence="California"> <Name>Chad Reed</Name> <Class>450</Class> <Brand>Yamaha</Brand> <Sponsors> <Name>ProTaper</Name> </Sponsors> </Rider> <Rider Residence="Mississippi"> <Name>Kevin Windham</Name> <Class>450</Class> <Brand>Honda</Brand> <Sponsors> <Name>Factory Connection</Name> </Sponsors> </Rider> <Rider Residence="Florida"> <Name>James Stewart</Name> <Class>450</Class> <Brand>Kawasaki</Brand> <Sponsors> <Name>Renthal</Name> </Sponsors> </Rider> </Riders> Isn’t that much easier than using the DOM? 793 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 794 Part V Data Connectivity What about querying XML? Open Notepad or another text editor and type in the following: <Employees> <Employee id="1" Dept="0001"> <Name>Scott</Name> <Address> <Street>555 Main St.</Street> <City>Wellington</City> <State>FL</State> </Address> <Title>All Things Techy</Title> <HireDate>02/05/2007</HireDate> <Gender>M</Gender> </Employee> <Employee id="2" Dept="0005"> <Name>Steve</Name> <Address> <Street>444 Main St.</Street> <City>Snahomish</City> <State>WA</State> </Address> <Title>Mr. SciFi</Title> <HireDate>05/14/2002</HireDate> <Gender>M</Gender> </Employee> <Employee id="3" Dept="0004"> <Name>Joe</Name> <Address> <Street>222 Main St.</Street> <City>Easley</City> <State>SC</State> </Address> <Title>All Things Bleeding Edge</Title> <HireDate>07/22/2004</HireDate> <Gender>M</Gender> </Employee> </Employees> Save this file as Employees.xml. Next, add the following code behind button2 : XElement employees = XElement.Load("Employees.xml"); textBox1.Text = employees.Element("Employee").ToString(); Run the project again and click button2. The text box will be filled with the first employee from the XML document, as shown here: <Employee id="1" Dept="0001"> <Name>Scott</Name> 794 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 795 LINQ 34 <Address> <Street>555 Main St.</Street> <City>Wellington</City> <State>FL</State> </Address> <Title>All Things Techy</Title> <HireDate>02/05/2007</HireDate> <Gender>M</Gender> </Employee> However, another way to return the first employee is to use the First() property to manually select the first employee element: employees.Elements("Employee").First() Another alternative is to use the ElementAt() method to specify which element to return: employees.Elements("Employee").ElementAt(0) Yet another method is to use a LINQ query expression: XElement empnum2 = (from emp in employees.Elements("Employee") where (int)emp.Attribute("id") == 2 select emp).ElementAt(0); This example uses a LINQ query expression to query the XML document, using the attribute "id" as a filter. In this example, the ElementAt() method is used to return the first element that matches the specified criteria. This next example uses a LINQ query expression to query the XML document and return the values of all the Name elements for each employee. To get that information, the descendants() method is used to return a collection of all descendants for the selected element: IEnumerable<string> empNames = from emp in employees.Descendants("Name") orderby emp.Value select emp.Value; foreach (string name in empNames) listBox1.Items.Add(name); When run, this example returns the following: Joe Scott Steve 795 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 796 Part V Data Connectivity Traversing XML Traversing XML in an XML tree using LINQ to XML is quite simple. Just use the methods of the XElement and XAttribute classes as necessary. Basically, the Elements and Element methods provide all of the element children of an XContainer (an XElement or XDocument) object. Using the XName object, such as Element(XName), one can return elements of that specific XName. For example, using the XML document used in the previous example, one can ‘‘walk the XML tree,’’ as shown here: employees.Element("Employees").Element("Employee") employees.Element("Employees").Element("Employee") .Element("Name") The same can be accomplished with attributes as well. The following illustrates how to walk an XML tree to get to a specific attribute: employees.Elements("Employee").ElementAt(1).Attribute("id") The thing to remember is that the Nodes(), Elements(), Element(Name),andElements(Name) methods provide the foundation and basic functionality of XML tree navigation. LINQ to DataSet Most, if not all, .NET developers are familiar with the concept of a DataSet because it is one of the most frequently used components in ADO.NET. A DataSet is a representation of the tables and rela- tionships found in a database, exposing a hierarchical object model made of objects such as tables, rows, columns, constraints, and relationships. The Dataset itself is extremely flexible and powerful. It provides the capability for applications to efficiently work with a subset of data found in a database and to manipulate the data as needed by the application, all while in a disconnected state. Yet, with all the flexibility there has not been, up to this point, a means or method for querying data contained within the DataSet (other than the few methods on the DataTable class such as Select, GetParentRow,andGetChildRow). This is where LINQ and LINQ to DataSet come in. With the querying power of LINQ, LINQ to DataSet provides developers with a full set of query capabilities to quickly and easily query the contents of a DataSet. An illustration of how easy it is to query a DataSet using LINQ to DataSet follows. Querying a DataSet using LINQ to DataSet First, create a new Visual Studio project. Make sure that the project includes references to both the System.Core and System.Data.DataSetExtension namespaces. On the form, place a button, a text box, and a list box. Add the following using statement to the code as well: using System.Data.SqlClient; 796 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 797 LINQ 34 Anyone who has worked with a DataSet is intimately familiar with how to populate it using the SqlDataAdapter. The following example uses the SqlDataAdapter to populate a DataSet with data from the Person.Contact table then use a LINQ query expression to query the DataSet. Add the following code to the Click event of the button: try { //first, populate the dataset DataSet ds = new DataSet(); string connectionInfo = "Data Source=SERVERNAME;Initial Catalog=AdventureWorks;Integrated Security=sspi"; SqlDataAdapter da = new SqlDataAdapter("select pc.ContactID, pc.FirstName, pc.MiddleName, pc.LastName, pc.EmailAddress from Person.Contact pc", connectionInfo); da.TableMappings.Add("Table", "Contact"); da.Fill(ds); DataTable dt = ds.Tables["Contact"]; textBox1.Text = ds.Tables[0].Rows.Count.ToString(); //now query it for specific people var con = from contact in dt.AsEnumerable() where contact.Field<string>("FirstName") .StartsWith("S") select new { ContactID = contact.Field<int> ("ContactID"), FirstName = contact.Field<string> ("FirstName"), MiddleName = contact.Field<string>("MiddleName"), LastName = contact.Field<string> ("LastName"), Email = contact.Field<string> ("EmailAddress")}; foreach (var cont in con) listBox1.Items.Add(cont.ContactID.ToString() + " " + cont.FirstName + " " + cont.MiddleName + " " + cont.LastName + " " + cont.Email); } 797 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 798 Part V Data Connectivity catch (Exception ex) { MessageBox.Show(ex.Message); } Compile the project to ensure there are no errors, and then run it. When the form opens, click the but- ton. The text box will display the total number of records contained in the DataSet. The list box will fill with all the contacts from the DataSet whose first name begins with the letter S. Note a couple of things here: ■ The DataSet is not a typed DataSet because the Field() method is used to access the column values of the DataRow. ■ The LINQ query itself. Like the others before it in this chapter, the syntax follows the same pattern as the other queries. The only difference is the data source. ■ The use of the AsEnumerable() method. This method returns an IEnumerable object, which in this case enables you to iterate over the data in the DataTable. The next example does two things. One, it uses a typed DataSet. Two, it adds a second table to the DataSet that illustrates how to use a LINQ query to query data from two tables. Add a new item to the solution, this time adding a DataSet. In the Add New Item dialog, select Data from the list of Categories, and then select DataSet from the list of Templates. Name the DataSet ContactDS .xsd and click OK. The DataSet Designer will display in the Visual Studio IDE. The DataSet Designer is a visual tool for creating and editing typed DataSets and the individual items that make up DataSets. The DataSet Designer provides developers with a visual depiction of the objects contained in the DataSets. In the Dataset Designer, drag and drop the Contact and Employee tables from the Server Explorer. Next, modify the code behind the button as follows: try { ContactsDS ds = new ContactsDS(); string connectionInfo = "Data Source=SERVERNAME;Initial Catalog=AdventureWorks;Integrated Security=sspi"; SqlDataAdapter da = new SqlDataAdapter("select * from Person .Contact; select * from HumanResources.Employee;", connectionInfo); da.TableMappings.Add("Table", "Contact"); da.TableMappings.Add("Table1", "Employee"); da.Fill(ds); textBox1.Text = "contact table has " + ds.Tables[0].Rows.Count .ToString() + " records, and employee table has " + 798 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 799 LINQ 34 ds.Tables[1].Rows.Count.ToString() + " records."; var query = from con in ds.Contact join emp in ds.Employee on con.ContactID equals emp.ContactID where con.FirstName.StartsWith("S") select new { ContactID = con.ContactID, FirstName = con.FirstName, LastName = con.LastName, EMail = con.EmailAddress, EmpID = emp.EmployeeID, IDNum = emp.NationalIDNumber, HireDate = emp.HireDate }; foreach (var cont in query) listBox1.Items.Add(cont.ContactID.ToString() + " " + cont.FirstName + " " + cont.LastName + " " + cont.EMail + " " + cont.EmpID + " " + cont.IDNum + " " + cont.HireDate.ToString()); } catch (Exception ex) { MessageBox.Show(ex.Message); } Compile the project to ensure there are no coding errors, and then run it. When the form displays, click the button. The text box will display a message indicating the number of records returned in each table. The list box will then display a combination of contact and employee data for all contacts whose first name begins with the letter S. Before moving on to the last example, consider what the code is doing. In many ways it is very similar to the previous example, but several things are different: ■ This example uses a typed DataSet. ■ Two tables are being populated, not just one. ■ Because a typed DataSet is being used, it’s no longer necessary to use the Field<> method. Instead, it employs a direct mapping to the tables and columns and the use of IntelliSense. ■ The LINQ query expression uses the join standard query operator to join the Contact table and the Employee table. 799 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 800 Part V Data Connectivity One more comment about the prior example. Because a typed DataSet was used, the DataSet itself contains the relationship information. This can be visually seen on the DataSet Designer. If a typed dataset had not been used, then the relationship would have to be defined programmatically using the DataRelation class. Data binding with LINQ to DataSet This section describes how to use LINQ to DataSet to do data binding. This example is similar to the first, in that it uses an untyped DataSet, but this is not the focus of the example because a typed DataSet can still be used. The important thing to learn from this example is how to do data binding using LINQ to DataSet. For this example, open the form in design view. From the toolbox, drag and drop a DataGridView control onto the form. Place another button on the form and enter the following code in its Click event: try { DataSet ds = new DataSet(); string connectionInfo = "Data Source=SERVERNAME;Initial Catalog=AdventureWorks;Integrated Security=sspi"; SqlDataAdapter da = new SqlDataAdapter("select pc.ContactID, pc.FirstName, pc.MiddleName, pc.LastName, pc.EmailAddress from Person.Contact pc", connectionInfo); da.TableMappings.Add("Table", "Contact"); da.Fill(ds); DataTable dt = ds.Tables["Contact"]; textBox1.Text = ds.Tables[0].Rows.Count.ToString(); IEnumerable<DataRow> contact = from con in dt.AsEnumerable() where con.Field<string>("FirstName").StartsWith("S") orderby con.Field<string>("LastName") select con; DataTable partialTable = contact.CopyToDataTable<DataRow>(); DataView dv = new DataView(partialTable); dataGridView1.DataSource = dv; } catch (Exception ex) { MessageBox.Show(ex.Message); } 800 www.getcoolebook.com Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 801 LINQ 34 This example populates a DataSet with all the data from the Contacts table and then queries the DataSet using a LINQ query. The query returns an enumeration of DataRow objects that is used to populate a DataTable via the CopyToDataTable() method. Once the DataTable is populated, anew DataView is created and populated with the data in the DataTable.TheDataView is then assigned to the DataSource property of the DataGridView. Another option for binding would have been to bind the DataTable directly to the DataGridView, as follows: dataGridView1.DataSource = partialTable; However, the DataView provides additional capabilities such as sorting and filtering data that is stored in a DataTable. Additionally, the data can be bound by implicitly binding data to controls, by implementing the IListSource interface. This interface enables an object to return a list that is bindable to a data source, as shown here: var query = from c in contact where c.Field<string>("FirstName").StartsWith("S") orderby c.Field<string>("LastName") select c; dataGridView1.DataSource = query; Likewise, the following can be done: BindingSource bindsrc = new BindingSource(); bindsrc.DataSrouce = query; dataGridView1.DataSource = bindsrc; Implicit binding is available because the Table<T> and DataQuery<T> classes have been updated to implement the IListSource interface. All of these examples populated the initial DataSet with all of the records from the Contacts table. This was done by design to illustrate the querying capabilities of LINQ to DataSet. LINQ to Entities When talking about LINQ to Entities, the discussion must also discuss the ADO.NET Entity Framework. This is because LINQ to Entities is the part of the ADO.NET Entity Framework that provides the LINQ query capabilities. That statement then begs the question, what is the difference between LINQ to SQL and LINQ to Enti- ties? In the LINQ to SQL section, it was stated that LINQ to SQL is an object-relational mapping frame- work that enables the direct 1-to-1 mapping of SQL Server database objects to .NET classes, where the database closely resembles the application model. 801 www.getcoolebook.com . to Enti- ties? In the LINQ to SQL section, it was stated that LINQ to SQL is an object-relational mapping frame- work that enables the direct 1-to-1 mapping of SQL Server database objects to .NET. DataSet(); string connectionInfo = "Data Source=SERVERNAME;Initial Catalog=AdventureWorks;Integrated Security=sspi"; SqlDataAdapter da = new SqlDataAdapter("select pc.ContactID, pc.FirstName,. DataSet(); string connectionInfo = "Data Source=SERVERNAME;Initial Catalog=AdventureWorks;Integrated Security=sspi"; SqlDataAdapter da = new SqlDataAdapter("select pc.ContactID, pc.FirstName,