256 Microsoft ADO.NET 4 Step by Step Running Entity SQL Queries The Entity Framework includes two key ways of using Entity SQL queries to access entity- managed data: using an ObjectQuery instance to query the entities within the context directly or using a more traditional ADO.NET provider-like interface. Running Queries Using an ObjectQuery The System.Data.Objects.ObjectQuery(Of T) class-processes an Entity SQL statement against an open EF context and returns the results as a collection of either named or anonymous instances. C# // SalesOrderEntities is an Entity Container. using (SalesOrderEntities context = new SalesOrderEntities(GetConnectionString())) { ObjectQuery<Customer> query = New ObjectQuery<Customer>(sqlText, context); // Other code as needed } Visual Basic ' SalesOrderEntities is an Entity Container. Using context As New SalesOrderEntities(GetConnectionString()) Dim query As New ObjectQuery(Of Customer)(sqlText, context) ' Other code as needed End Using You must keep the context around as long as you need to access the ObjectQuery object’s data, especially if the retrieved data includes content accessed through a navigation property. The generic ObjectQuery type works like a typical generic collection object, but it’s not a true collection. When you create the ObjectQuery instance, the Entity Framework delays process- ing of the query until you specifically request data. Even then, it might decide to retrieve only the requested portion of the data. Keeping the context around during the entire data- retrieval process enables the ObjectQuery to fulfill any data requests you give it over time. Chapter 15 Querying Data in the Framework 257 Note The object context and its related entity container in the conceptual model expose a LazyLoadingEnabled property. Changing this Boolean value alters the way that the Entity Framework loads data at the other end of a navigation property. Models built with the visual de- signer set this property to True by default, keeping unused navigation property data unloaded. Setting this value to False (the default for manually created models) provides more proactive loading of related data and might allow you to access such data even when the context is no longer available. You can adjust this property’s value in the visual designer or within the context instance. The preceding code creates an instance of ObjectQu ery with a generic focus of Customer, presumably one of the entities in the Entity Data Model. The Entity SQL statement used to retrieve the results must generate entities of that type. Your code can also generate data val- ues not tied to any predefined entity or custom type. These anonymous-type queries use the System.Data.Common.DbDataRecord class as the target generic type. C# ObjectQuery<Customer> query = New ObjectQuery<DbDataRecord>(sqlText, context); Visual Basic Dim query As New ObjectQuery(Of DbDataRecord)(sqlText, context) Retrieving Entity Data Through an ObjectQuery : C# 1. Open the “Chapter 15 CSharp” project from the installed samples folder. The project includes a Windows.Forms class named EntityQuery, which is a tool for trying out EF queries. 2. Open the source code view for the EntityQ uery form. Locate the GetConnec tionString 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. 258 Microsoft ADO.NET 4 Step by Step 3. Locate the ActSingleEntity_Click event handler. This routine creates an Ob jectQuery to retrieve Customer entities. Just after the “Retrieve the customer entities via a query” comment, within the try block, add the following statements: ActiveContext = new SalesOrderEntities(GetConnectionString()); sqlText = @"SELECT VALUE Customer FROM Customers AS Customer ORDER BY Customer.FullName DESC"; query = new ObjectQuery<Customer>(sqlText, ActiveContext); This code creates a context (ActiveContext) and then creates an ObjectQuery pseudo- collection for Customer entities. To guard against errors related to lazy loading of data, the context remains open after this code completes. 4. Run the program. Click the Single Entity button to view the results of this example’s query. Retrieving Entity Data Through an ObjectQuery : Visual Basic 1. Open the “Chapter 15 VB” project from the installed samples folder. The project in- cludes a Windows.For ms class named EntityQuery, which is a tool for trying out EF queries. 2. Open the source code view for the EntityQ uery form. Locate the GetConnec tionString 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: Chapter 15 Querying Data in the Framework 259 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 ActSingleEntity_Click event handler. This routine creates an Ob jectQuery to retrieve Customer entities. Just after the “Retrieve the customer entities via a query” comment, within the Try block, add the following statements: ActiveContext = New SalesOrderEntities(GetConnectionString()) sqlText = "SELECT VALUE Customer FROM Customers " & "AS Customer ORDER BY Customer.FullName DESC" query = New ObjectQuery(Of Customer)(sqlText, ActiveContext) This code creates a context (ActiveContext) and then creates an ObjectQuery pseudo- collection for Customer entities. To guard against errors related to lazy loading of data, the context remains open after this code completes. 4. Run the program. Click the Single Entity button to view the results of this example’s query. 260 Microsoft ADO.NET 4 Step by Step Running Queries Using a Provider In standard ADO.NET data processing, SQL-based queries make their way to the target data through command and connection objects, and then ultimately through a provider such as the SQL Server ADO.NET provider. The Entity Framework hosts its own data provider: the EntityClient provider. This provider exposes much of the same connection and command functionality available with the SQL Server and other native providers, but with the ability to query against entities in an Entity Data Model. The key classes for the EntityClient provider appear in the System.Data. EntityClient namespace. Using the EntityClient provider to query data covers the same general steps as are performed with other providers: 1. Create and open a connection using the EntityConnection class and a connection string. 2. Create an EntityCommand instance and then add the connection and the Entity SQL statement to it. 3. If the query contains @-prefixed parameters, add parameters objects as needed. 4. Call one of the command object’s Execute methods to process the query and return data results. The command object includes the ExecuteNonQuery method for running queries with no return results; the ExecuteScalar method, which returns a single result; and ExecuteReader, which returns a single-pass data reader, EntityDataReader. What is missing is the data adapter with its capability to move incoming data into a DataTable or DataSet instance. Considering all the other data-manipulation tools included with the Entity Framework, this is a small omission. But if you need to push entity data into a standard ADO.NET structure, you will have to do so manually. When using the EntityCommand.ExecuteReader method to generate a data reader, you must pass CommandBehavior.SequentialAccess as a behavior argument. C# results = query.ExecuteReader(CommandBehavior.SequentialAccess); Visual Basic results = query.ExecuteReader(CommandBehavior.SequentialAccess) Chapter 15 Querying Data in the Framework 261 This option is normally used when retrieving binary large objects (BLOBs) and other large content blocks from the database, but its use is required with the EntityClient provider, even when returning minimal content. The side effect of using the sequential access option is that each returned row’s data values must be retrieved in the same order in which they appear in the SQL statement and can be accessed only once each. After you read a field, it’s time to move on to the next one. The following exercise shows the EntityClient provider in action, using a parameterized query and a data reader to shuttle results into a DataTable instance. Retrieving Entity Data Through a Provider: C# Note This exercise continues the previous exercise in this chapter. 1. Locate the ActDataTable_Click event handler; this is a routine that copies entity-based data into a standard ADO.NET DataTable instance. Because the data will be shuttled manually into an existing data table, the routine includes code to build the receiving table. resultsAsTable = new DataTable; resultsAsTable.Columns.Add("CustomerID", typeof(long)); resultsAsTable.Columns.Add("CustomerName", typeof(string)); resultsAsTable.Columns.Add("AnnualFee", typeof(decimal)); Most of the routine is contained within a using block that manages the provider connection. using (EntityConnection linkToDB = new EntityConnection(GetConnectionString())) { // Most of the code appears here. } 2. Just after the “Retrieve the data via a parameterized query” comment, add the follow- ing statement: sqlText = @"SELECT CU.ID, CU.FullName, CU.AnnualFee FROM SalesOrderEntities.Customers AS CU WHERE CU.AnnualFee >= @MinFee ORDER BY CU.FullName"; The EntityClient provider supports parameterized queries, as shown in these lines. 3. In the same section of code, within the try block that follows the newly added SQL statement, add these lines: query = new EntityCommand(sqlText, linkToDB); query.Parameters.AddWithValue("MinFee", 200); results = query.ExecuteReader(CommandBehavior.SequentialAccess); As mentioned previously, the CommandBehavior.SequentialAccess option is required. 262 Microsoft ADO.NET 4 Step by Step 4. Just after the “Move each row into the DataTable” comment, within one of the later try blocks, add the following code: while (results.Read()) { oneRow = resultsAsTable.NewRow(); oneRow["CustomerID"] = (long)results["ID"]; oneRow["CustomerName"] = (string)results["FullName"]; oneRow["AnnualFee"] = (decimal)results["AnnualFee"]; resultsAsTable.Rows.Add(oneRow); } These lines move the data from the reader into the preconfigured DataTable instance. As is required by the sequential access flag used when creating the reader, the incom- ing fields are accessed in the order in which they appeared in the SQL query, and each field is accessed only once. 5. Run the program. Click the Data Table button to view the results of this example’s query. Chapter 15 Querying Data in the Framework 263 Retrieving Entity Data Through a Provider: Visual Basic Note This exercise continues the previous exercise in this chapter. 1. Locate the ActDataTable_Click event handler; this is a routine that copies entity-based data into a standard ADO.NET DataTable instance. Because the data will be shuttled manually into an existing data table, the routine includes code to build the receiving table. resultsAsTable = New DataTable resultsAsTable.Columns.Add("CustomerID", GetType(Long)) resultsAsTable.Columns.Add("CustomerName", GetType(String)) resultsAsTable.Columns.Add("AnnualFee", GetType(Decimal)) Most of the routine is contained within a Using block that manages the provider connection. Using linkToDB As New EntityConnection(GetConnectionString()) ' Most of the code appears here. End Using 2. Just after the “Retrieve the data via a parameterized query” comment, add the follow- ing statement: sqlText = "SELECT CU.ID, CU.FullName, CU.AnnualFee " & "FROM SalesOrderEntities.Customers AS CU " & "WHERE CU.AnnualFee >= @MinFee ORDER BY CU.FullName" The EntityClient provider supports parameterized queries, as shown in these lines. 3. In the same section of code, within the Try block that follows the newly added SQL statement, add these lines: query = New EntityCommand(sqlText, linkToDB) query.Parameters.AddWithValue("MinFee", 200) results = query.ExecuteReader(CommandBehavior.SequentialAccess) As mentioned previously, the CommandBehavior.SequentialAccess option is required. 4. Just after the “Move each row into the DataTable” comment, within one of the later Try blocks, add the following code: Do While (results.Read() = True) oneRow = resultsAsTable.NewRow() oneRow!CustomerID = CLng(results!ID) oneRow!CustomerName = CStr(results!FullName) oneRow!AnnualFee = CDec(results!AnnualFee) resultsAsTable.Rows.Add(oneRow) Loop 264 Microsoft ADO.NET 4 Step by Step These lines move the data from the reader into the preconfigured DataTable instance. As is required by the sequential access flag used when creating the reader, the incom- ing fields are accessed in the order in which they appeared in the SQL query, and each field is accessed only once. 5. Run the program. Click the Data Table button to view the results of this example’s query. Summary This chapter reviewed the Entity SQL language and its usage within .NET applications. Entity SQL is built with the same basic query language syntax found in SQL Server’s Transact-SQL and in other variations of SQL. Although there are some differences when dealing with the object nature of the Entity Framework, teams already working with SQL will have little trou- ble integrating Entity SQL into their applications. Although Entity SQL is great for organizations that have a large investment in SQL technolo- gies, it might not be the most straightforward EF-query tool for your needs. The upcoming chapters introduce additional ways that Entity Framework data can be accessed within your software and your business logic. Chapter 15 Querying Data in the Framework 265 Chapter 15 Quick Reference To Do This Select entity records using Entity SQL Write your query using the Entity SQL language. Create an instance of the entity context. Create an instance of ObjectQuery<class>, where class is an entity type, EF custom type, or DbDataRecord. Access the members of the ObjectQuery instance. Select the ID of all Product entity instances Use a query similar to the following: SELECT p.ID FROM Products AS p If the collection of entities was designed with a plural name, use that plural name in the query. Select a single value as a data-type value instead of as a row containing that value Use the SELECT VALUE syntax. . button to view the results of this example’s query. 260 Microsoft ADO. NET 4 Step by Step Running Queries Using a Provider In standard ADO. NET data processing, SQL-based queries make their way to. resultsAsTable.Rows.Add(oneRow) Loop 2 64 Microsoft ADO. NET 4 Step by Step These lines move the data from the reader into the preconfigured DataTable instance. As is required by the sequential access flag. "StepSample"; sqlPortion.IntegratedSecurity = true; Adjust these statements as needed to provide access to your own test database. 258 Microsoft ADO. NET 4 Step by Step 3. Locate the ActSingleEntity_Click