306 On the DataRow side, a new Field(Of T) method moves each data column value from a ge- neric, semi-typeless existence to a strongly typed presence within your queries. It is still up to you, as the programmer, to correctly indicate the type of each field as you add them to the LINQ query syntax. But once defined, you can apply all the standard operators to those fields, including them in projections, filters, and other types of expressions. Note You can use DataRow values within LINQ queries without applying the Field(Of T) exten- sion method. However, these fields will still pose as System.Object instances. This might prevent you from carrying out certain types of query actions on specific fields. Also, you must still resolve the data type of each field before using it in post-query processing. LINQ to DataSet also lets you craft queries that use ADO.NET Typed Data Sets; however, the Entity Framework supercedes most of the advantages of typed data sets. Therefore, LINQ queries against typed data sets are not discussed in this book. Writing Queries with LINQ to DataSet With the exception of the new enumerated methods specific to LINQ to DataSet, using ADO.NET DataTable objects in LINQ queries is identical to using standard collection objects. The first step involves converting a data table to its enumerable equivalent using the and, which can be applied to any DataTable instance. C# // Customer is an existing DataTable instance. var results = from cu in Customer.AsEnumerable() select cu; Visual Basic ' Customer is an existing DataTable instance. Dim results = From cu In Customer.AsEnumerable() Select cu Although the LINQ to DataSet provider includes “DataSet” in its name, the focus in LINQ queries is on the DataTable class. LINQ to DataSet does not consider a DataTable instance’s presence in an overall DataSet to be significant, nor does it examine any DataRelationship objects when processing queries that contain multiple DataTable instances. You must link tables together using LINQ’s standard Join operator or use the Where clause to create an implicit join. Chapter 18 Using LINQ to DataSet 307 C# // Explicit join. var results = from cu in Customer.AsEnumerable() join ord in Order.AsEnumerable() on cu.ID equals ord.CustomerID select // Implicit join var results = from cu in Customer.AsEnumerable() from ord in Order.AsEnumerable() where cu.ID == ord.CustomerID select Visual Basic ' Explicit join. Dim results = From cu In Customer.AsEnumerable() Join ord In Order.AsEnumerable() On cu.ID Equals ord.CustomerID Select ' Implicit join Dim results = From cu In Customer.AsEnumerable(), ord In Order.AsEnumerable() Where cu.ID = ord.CustomerID Select After making the tables part of the query, you can access each row’s individual column values as if they were typical LINQ query properties. As mentioned previously, LINQ will not auto- matically ascertain the data type of any given column; you must explicitly cast each field to its proper type. To cast a field, add the Field extension method to the end of the range variable (the range variables in the previous code sample are cu and ord). Because the implementation of Field uses generics, you must also attach a type name using the language-appropriate syntax. Pass the name of the column as an argument to Field. C# var results = from cu in Customer.AsEnumerable() orderby cu.Field<string>("FullName") select new { CustomerName = cu.Field<string>("FullName") }; Visual Basic Dim results = From cu In Customer.AsEnumerable() Select CustomerName = cu.Field(Of String)("FullName") Order By CustomerName 308 Microsoft ADO.NET 4 Step by Step The Field method includes a few overloaded variations. In addition to field names, you can use a zero-based column position to locate field data, although this might reduce readability in your queries. An additional argument lets you specify the DataRow Version to use. By de- fault, queries use the DataRowVersion.Current version of the row. Even when enumerated DataTable objects play a key role in a LINQ query, they need not be the only source of data involved. Part of LINQ’s appeal is that it allows you to write queries that involve data from disparate sources. You can mix LINQ to Objects and LINQ to DataSet content in the same query simply by including each source in the From clause. C# // Build an ad hoc collection, although you could also // include a fully realized class. var statusTable[] = { new { Code = "P", Description = "Active Order" }, new { Code = "C", Description = "Completed / Shipped" }, new { Code = "X", Description = "Canceled" }}; // Link ADO.NET and Object collections in one query. var results = from ord in Order.AsEnumerable() join sts in statusTable on ord.Field<string>("StatusCode") equals sts.Code orderby ord.Field<long>("ID") select new { OrderID = ord.Field<long>("ID"), CurrentStatus = sts.Description }; Visual Basic ' Build an ad hoc collection, although you could also ' include a fully realized class. Dim statusTable = {New With {.Code = "P", .Description = "Active Order"}, New With {.Code = "C", .Description = "Completed / Shipped"}, New With {.Code = "X", .Description = "Canceled"}} ' Link ADO.NET and Object collections in one query. Dim results = From ord In Order.AsEnumerable() Join sts In statusTable On _ ord.Field(Of String)("StatusCode") Equals sts.Code Select OrderID = ord.Field(Of Long)("ID"), CurrentStatus = sts.Description Order By OrderID As in LINQ to Objects, the actual processing of a LINQ to DataSet query does not occur until your code references content from a constructed query. However, all the involved DataTable instances must already be filled with valid data before you make the query. When dealing Chapter 18 Using LINQ to DataSet 309 with data from external sources, you must bring any data you plan to include in a LINQ query into the relevant DataTable instances before passing the objects through LINQ. If you use a D ataAdapter to load data, call its Fill method before using LINQ to extract data. Note The DataAdapter object’s Fill method loads all requested data into local Da taSet memory. If the tables you need to query with LINQ are large and you aren’t able to first reduce the num- ber of ADO.NET-managed rows, you might wish to consider alternatives to LINQ to DataSet. LINQ to Entities, discussed in Chapter 19, “Using LINQ to Entities,” can process external data without the need to load full tables into memory. Querying with LINQ to DataSet: C# 1. Open the “Chapter 18 CSharp” project from the installed samples folder. The project includes a Windows.Forms class named OrderViewer, which is a simple order-list viewer. 2. Open the source code view for the OrderVie wer form. Locate the GetConnectionString function; this is a routine that uses a SqlConnectionStringBuilder to create a valid con- nection string to the sample database. It currently includes the following statements: sqlPortion.DataSource = @"(local)\SQLExpress"; sqlPortion.InitialCatalog = "StepSample"; sqlPortion.IntegratedSecurity = true; Adjust these statements as needed to provide access to your own test database. 3. Locate the ActView_Click event handler. This routine displays a list of orders for either all customers in the database or for a specific customer by ID number. Just after the “Retrieve all customer orders” comment, add the following statement: var result = from cu in customerTable.AsEnumerable() from ord in orderTable.AsEnumerable() from sts in statusTable where cu.Field<long>("ID") == ord.Field<long>("Customer") && ord.Field<string>("StatusCode") == sts.Code orderby cu.Field<string>("FullName"), ord.Field<long>("ID") select new { CustomerID = cu.Field<long>("ID"), CustomerName = cu.Field<string>("FullName"), OrderID = ord.Field<long>("ID"), OrderStatus = sts.Description, OrderDate = ord.Field<Date>("OrderDate"), OrderTotal = ord.Field<decimal>("Total") }; This query combines two DataTable instances (customerTable and orderTable, each decorated with the AsEnume rable extension method) with a collection of local object instances (stat usTable). It forms implicit inner joins between the tables via the where clause and performs a projection of fields from each source table. 310 Microsoft ADO.NET 4 Step by Step 4. Just after the “Filter and display the orders” comment, add the following lines: var result2 = result.Where(ord => ord.CustomerID == long.Parse(CustomerID.Text)); AllOrders.DataSource = result2.ToList(); These statements filter the original query by selecting those records that include a user- specified customer ID. This segment uses LINQ extension methods and a lambda ex- pression, which works well with the LINQ to DataSet provider. The second line displays the results. 5. Just after the “Just display the original full results” comment, add the following statement: AllOrders.DataSource = result.ToList(); This code displays the query results when no further customer ID filtering is needed. 6. Run the program. To see orders, select the Include One Customer By ID option, enter 1 in the Customer ID field, and then click View. The grid displays content from each of the three source tables. For example, the CustomerName column shows a value from the ADO.NET Customer table, the OrderDate column comes from the Orde r table, and O rderStatus gets its information from the ad hoc in-memory statusTable collection. Querying with LINQ to DataSet: Visual Basic 1. Open the “Chapter 18 VB” project from the installed samples folder. The project in- cludes a Windows.Forms class named OrderViewer, which is a simple order-list viewer. Chapter 18 Using LINQ to DataSet 311 2. Open the source code view for the OrderVie wer form. Locate the GetConnectionString function; this is a routine that uses a SqlConnectionStringBuilder to create a valid con- nection string to the sample database. It currently includes the following statements: sqlPortion.DataSource = "(local)\SQLExpress" sqlPortion.InitialCatalog = "StepSample" sqlPortion.IntegratedSecurity = True Adjust these statements as needed to provide access to your own test database. 3. Locate the ActView_Click event handler. This routine displays a list of orders for either all customers in the database or for a specific customer by ID number. Just after the “Retrieve all customer orders” comment, add the following statement: Dim result = From cu In customerTable.AsEnumerable(), ord In orderTable.AsEnumerable(), sts In statusTable Where cu.Field(Of Long)("ID") = ord.Field(Of Long)("Customer") _ And ord.Field(Of String)("StatusCode") = sts.Code Select CustomerID = cu.Field(Of Long)("ID"), CustomerName = cu.Field(Of String)("FullName"), OrderID = ord.Field(Of Long)("ID"), OrderStatus = sts.Description, OrderDate = ord.Field(Of Date)("OrderDate"), OrderTotal = ord.Field(Of Decimal)("Total") Order By CustomerName, OrderID This query combines two DataTable instances (customerTable and orderTable, each decorated with the AsEnume rable extension method) with a collection of local object instances (stat usTable). It forms implicit inner joins between the tables via the Where clause and performs a projection of fields from each source table. 4. Just after the “Filter and display the orders” comment, add the following lines: Dim result2 = result.Where(Function(ord) ord.CustomerID = CLng(CustomerID.Text)) AllOrders.DataSource = result2.ToList() These statements filter the original query by selecting those records that include a user- specified customer ID. This segment uses LINQ extension methods and a lambda ex- pression, which works well with the LINQ to DataSet provider. The second line displays the results. 5. Just after the “Just display the original full results” comment, add the following statement: AllOrders.DataSource = result.ToList() This code displays the query results when no further customer ID filtering is needed. 312 Microsoft ADO.NET 4 Step by Step 6. Run the program. To see orders, select the Include All Customers option and then click View. The grid displays content from each of the three source tables. For example, the CustomerName column shows a value from the ADO.NET Customer table, the OrderDate column comes from the Order table, and OrderStatus gets its information from the ad hoc in-memory statusTable collection. Summary This chapter introduced LINQ to DataSet, an ADO.NET-focused variation of LINQ to Objects. The implementation of the LINQ to Dataset provider shares a close relationship and syntax with the base LINQ to Objects implementation. By applying a few simple extension methods, DataTable objects can become part of independent or integrated data queries. Beyond LINQ to Objects, LINQ to DataSet is probably the easiest of the LINQ providers to use in your application. Its only drawback is that it expects any queried data to be memory- resident, something not required by other LINQ providers that extract content from external databases. Chapter 18 Using LINQ to DataSet 313 Chapter 18 Quick Reference To Do This Include a DataTable instance in a LINQ query Call the DataTable object’s AsEnu merable extension method. Pass the results of this call as a table source in the query. Include a DataTable column in a LINQ query Add the DataTable as a source within the query, adding a range variable. Call the range variable’s Field extension method, indicat- ing the generic data type and the name of the column. Chapter 19 Using LINQ to Entities After completing this chapter, you will be able to: Create LINQ queries that access content from an Entity Framework data model Call database-level functions from LINQ queries Understand how LINQ treats entities differently from other data sources The Entity Framework (EF) is a model-based interface between your application and an ex- ternal database. As discussed earlier in this book, the Framework presents logical, database- centric data content in a conceptual form expressed through object instances. This focus on objects makes the Entity Framework a great match for LINQ. The LINQ to Entities provider brings the world of entities and LINQ queries together. This chapter introduces this EF-centric LINQ provider, a system that takes advantage of the Entity Framework’s model-focused data and LINQ’s capability to develop native language queries that target entities and their properties. Note The exercises in this chapter all use the same sample project, a tool that makes queries using LINQ to Entities. Although you can run the application after each exercise, the expected results for the full application might not appear until you complete all exercises in the chapter. Understanding the LINQ to Entities Provider The ObjectSet(Of TEntity) and ObjectQuery(Of T) classes—the two key collection-style base classes used in the Entity Framework—are ready to use in LINQ queries. Both collection types implement IEnumerable(Of T) and IQueryable(Of T), the same interfaces that LINQ requires to enable query operations on a collection. Any entity collection exposed by an EF context or built from an ObjectQuery instance—such as a query processed with Entity SQL— can form the basis of a LINQ query. EF entities referenced in a LINQ query expose properties that are available for projection, filtering, sorting, and other LINQ operations. LINQ to Entities queries are much cleaner than their LINQ to DataSet counterparts because each entity property already expresses its model-defined data type. . implicit inner joins between the tables via the where clause and performs a projection of fields from each source table. 310 Microsoft ADO. NET 4 Step by Step 4. Just after the “Filter and display. String)("FullName") Order By CustomerName 308 Microsoft ADO. NET 4 Step by Step The Field method includes a few overloaded variations. In addition to field names, you can use a zero-based column position to. filtering is needed. 312 Microsoft ADO. NET 4 Step by Step 6. Run the program. To see orders, select the Include All Customers option and then click View. The grid displays content from each of