276 Microsoft ADO.NET 4 Step by Step Note At this point, the Delete button on the CustomerEditor form does not work. The example that appears later in this chapter on page 283 adds the necessary code to enable the customer re- moval feature. Modifying a Database Through Entity Objects: Visual Basic 1. Open the “Chapter 16 VB” project from the installed samples folder. The project in- cludes Windows.Forms classes named CustomerEditor and CustomerDetail, which let a user modify records in the sample database’s Customer table. 2. Open the source code view for the CustomerEditor form. Locate the GetConnectionString function; this is a routine that uses a SqlConnectionStringBuilde r 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. Open the source code view for the CustomerDetail form. Locate the SaveFormData function. This routine updates an entity’s properties with data supplied by the user. Just after the “Update the individual fields” comment, add the following statements: toUpdate.FullName = CustomerName.Text.Trim If (Address1.Text.Trim.Length > 0) Then _ toUpdate.Address1 = Address1.Text.Trim Else _ toUpdate.Address1 = Nothing If (Address2.Text.Trim.Length > 0) Then _ toUpdate.Address2 = Address2.Text.Trim Else _ toUpdate.Address2 = Nothing If (CityName.Text.Trim.Length > 0) Then _ toUpdate.City = CityName.Text.Trim Else _ Chapter 16 Understanding Entities Through Objects 277 toUpdate.City = Nothing If (ItemData.GetItemData(StateName.SelectedItem) <> -1&) Then _ toUpdate.StateRegion = ItemData.GetItemData( StateName.SelectedItem) Else toUpdate.StateRegion = Nothing If (PostalCode.Text.Trim.Length > 0) Then _ toUpdate.PostalCode = PostalCode.Text.Trim Else _ toUpdate.PostalCode = Nothing If (PhoneNumber.Text.Trim.Length > 0) Then _ toUpdate.PhoneNumber = PhoneNumber.Text.Trim Else _ toUpdate.PhoneNumber = Nothing If (WebSite.Text.Trim.Length > 0) Then _ toUpdate.WebSite = WebSite.Text.Trim Else _ toUpdate.WebSite = Nothing toUpdate.AnnualFee = AnnualFee.Value Most of the properties in the Customer entity are nullable, allowing even numeric prop- erties to be assigned a value of Nothing. 4. Just after the “Update the database” comment, inside the Try block, add the following statements: If (Me.ActiveCustomer Is Nothing) Then Me.ActiveContext.Customers.AddObject(toUpdate) Me.ActiveCustomer = toUpdate End If Me.ActiveContext.SaveChanges() Return True These lines perform the actual add or update of the Customer entity. The call to SaveChanges flushes all changes out to the database. 5. Run the program. When the list of customers appears, click Add or select a customer and click Edit. When the CustomerDetail form appears, add or update the individual field values. When you’re finished, click OK. If you added or modified a customer name, that change will be reflected in the list of customer names back on the CustomerEditor form. 278 Microsoft ADO.NET 4 Step by Step Note At this point, the Delete button on the CustomerEditor form does not work. The example that appears later in this chapter on page 284 adds the necessary code to enable the customer re- moval feature. Using Query Builder Methods Entity SQL and entity-specific LINQ queries (discussed in upcoming chapters) are useful tools for accessing Entity Framework-managed data because they both provide a SQL-like experience, which is based, in part, on the desire to provide an English-like experience. As programmer-friendly as these methods are, they aren’t in a form that is easily processed by languages such as C# and Visual Basic. The SELECT statements and the pseudo-language content of a LINQ query must first be molded into a form that the relevant language com- piler can process. Enter query builder methods. Query builder methods are ordinary .NET extension methods that apply the subtasks of a SQL-query to an entity or to the larger entity collection. An extension method is a feature of .NET that lets developers add method-like functionality to a class without having access to the source code for that class. For example, the following code adds a DelimSubstring meth- od to the System.String data type that returns a delimited substring: C# public static class StringExtensions { // The "this" keyword defines the extension target. public static String DelimSubstring(this String origString, String delim, int position) { // Return a base-1 delimited substring from a larger string. String pieces[]; // Don't bother if there is no string to split. if (origString == null) return null; if (origString.Length == 0) return ""; if (position <= 0) return ""; // Break the string into delimited parts. pieces = origString.Split(new String[] {delim}, StringSplitOptions.None); // Locate and return the requested portion. if (pieces.Count < position) return ""; return pieces(position - 1); } } Chapter 16 Understanding Entities Through Objects 279 Visual Basic ' Namespace for the <Extension()> attribute. Imports System.Runtime.CompilerServices Module StringExtensions <Extension()> Public Function DelimSubstring(ByVal origString As String, ByVal delim As String, ByVal position As Integer) As String ' Return a base-1 delimited substring from a larger string. Dim pieces() As String ' Don't bother if there is no string to split. If (origString Is Nothing) Then Return Nothing If (origString.Length = 0) Then Return "" If (position <= 0) Then Return "" ' Break the string into delimited parts. pieces = origString.Split(New String() {delim}, StringSplitOptions.None) ' Locate and return the requested portion. If (pieces.Count < position) Then Return "" Return pieces(position - 1) End Function End Module To use an extension method, call it as a method on the target data type or class. C# String phoneNumber = "206-555-1234"; String areaCode = phoneNumber.DelimSubstring("-", 1); Visual Basic Dim phoneNumber As String = "206-555-1234" Dim areaCode As String = phoneNumber.DelimSubstring("-", 1) The Entity Framework includes extension methods for common Entity SQL clauses, includ- ing Select, GroupBy, and Union. These methods can be used on instances of ObjectSet and ObjectQuery, which are two Framework base classes (introduced earlier in this chapter) that act as collections for named or anonymous entity types. 280 Microsoft ADO.NET 4 Step by Step Consider this simple Entity SQL statement that selects a few fields from Customer entities: SELECT c.FullName, c.WebSite FROM Customers AS c ORDER BY c.FullName You can apply query builder methods to a set of customer entities to generate the same results. C# ObjectQuery<DbDataRecord> query = context.Customers.Select("it.FullName, it.WebSite").OrderBy("it.FullName"); Visual Basic Dim query As ObjectQuery(Of DbDataRecord) = context.Customers.Select("it.FullName, it.WebSite").OrderBy("it.FullName") This code uses the Select and OrderBy query builder methods to replicate the SELECT and ORDER BY Entity SQL clauses. In fact, internally the Entity Framework does something similar, breaking the clauses and components of a complex query into distinct method calls. (Both Entity SQL and LINQ convert their queries into a “command tree” that executes in much the same way as the query builder example shown here.) The “it” keyword used within the content of each query builder method provides a way of referring to the current named or anonymous type as viewed from each builder method. It’s somewhat similar to the this keyword in C#, or the Me keyword in Visual Basic, both of which allow classes to reference their own members. You can change the “it” term to something else by setting the Name property of the ObjectSet or ObjectQuery instance being enhanced by the extension method. All query builder methods return a generic ObjectQuery instance, either an entity type, a modeled complex type, or the ad hoc ObjectQuery(Of DbDataRecord) type. Table 16-1 lists the standard query builder methods provided by the Entity Framework. Chapter 16 Understanding Entities Through Objects 281 TABLE 16-1 Query Builder Methods Query Builder Method Entity SQL Equivalent Description Select SELECT Returns all original instances in the base class, but with only those properties specified in the string argument. The argument is a comma-delimited list of fields, properties, or expressions, as is normally used in a SELECT statement. SelectValue SELECT VALUE Similar to Select, but returns only a single property or value. The returned value must be a simple type, an EF- defined complex type, or a modeled entity type. Distinct SELECT DISTRICT Removes duplicates from the collection of data instances. Distinct doesn’t work on instances that contain BLOBs or other database-level large-data fields. Where WHERE Applies a conditional rule to the instances in the base query, returning only those that match. The string argument is the same as you would normally use in a WHERE clause. GroupBy GROUP BY Groups results by one or more fields. The method accepts two string arguments: (1) a comma-delimited list of the grouping fields, or what would normally appear in a SQL GROUP BY clause; and (2) a comma-delimited list of pro- jected fields, or what would normally appear in the SELECT clause. OrderBy ORDER BY Uses a comma-delimited list of property or field names to order a collection of instances. When used, the OrderBy method should always appear at the end of a statement that includes multiple query builder methods. Top TOP, LIMIT Returns just the top number of instances from the base collection, which is indicated by the passed-in argument. When used after (to the right of) the Skip method, Top acts like the LIMIT clause in an Entity SQL query. Skip SKIP Skips over the number of instances indicated by the passed-in count, returning all instances after the skipped portion. OfType OFTYPE For conceptual models that include inherited types, this method returns only those instances that are truly instances of the specified base or derived type. Union UNION Combines a base query with another passed-in query, re- turning the full set with duplicates removed. UnionAll UNION ALL Same as the Union method, but with duplicates retained. Intersect INTERSECT Merges the base query with the contents of another query argument and returns only those instances that are found in both. 282 Microsoft ADO.NET 4 Step by Step Query Builder Method Entity SQL Equivalent Description Except EXCEPT Returns all records in the original collection except those found in the collection passed as an argument to this method. query1.Except(query2) Returns query1 records not found in query2. Those query builder methods that accept property names or expressions can use @-prefixed parameters. The parameter values appear as additional arguments to the query method. The following statement uses a parameter in a Where method call: C# query = context.Customers.Where("it.ID = @lookupID", new ObjectParameter("lookupID", 3422)); Visual Basic query = context.Customers.Where("it.ID = @lookupID", New ObjectParameter("lookupID", 3422)) When using the Union, UnionAll, Intersect, or Except methods, the parameters applied to the two source query collections are merged. Thus the same parameter name cannot appear in both source queries. This restriction on duplicate parameter names exists because of the way query builder meth- ods process the core data. As with Entity SQL processing, the query builder methods delay processing of data until your code attempts to access actual data from the query, such as try- ing to access the value of an individual entity property. Instead of applying changes to the entity sets and instance collections as the query builder methods are encountered, each method instead helps craft one or more database-level queries that perform the actual data retrieval. You can view this composed SQL statement by accessing the CommandText property of the final query builder method in your set. As an ex- ample, here’s the SQL command text produced by the previous query (the one that includes the @lookupID parameter): SELECT VALUE it FROM ([SalesOrderEntities].[Customers]) AS it WHERE it.ID = @lookupID Chapter 16 Understanding Entities Through Objects 283 Queryable Extension Methods The collection of results returned by each of the query builder methods is ObjectQuery(Of T), a generic type that implements the IQueryable interface. This interface supports its own set of extension methods (from the System.Linq.Queryable type) that can be used to augment the results returned by query builder methods. One of the simplest of these methods is First, which returns the topmost instance in the query collection. It is the same as using the Top query builder method, but it returns results of type IQueryable. Other Queryable methods, such as Count and Max, provide results that parallel the Entity SQL aggregate functions. For a full list of these methods, look up the “Queryable Class (System.Linq)” entry in the Visual Studio online help. Note Many of the Queryable extension methods accept lambda functions as arguments. Lambdas act as a type of inline function that the extension method calls for each instance in the collection being processed. Visual Basic and C# have specific syntax rules for crafting lambda expressions. See the Visual Studio online help for information on using lambda expressions. Using Query Builder Methods: C# Note This exercise continues the previous exercise in this chapter. 1. Open the source code view for the CustomerEditor form. Locate the ActDelete_Click event handler; this is a routine that manages the removal of Customer records. Just af- ter the “Locate the customer record” comment (but before the if statement that follows it), add the following code: whichCustomer = ActiveContext.Customers.Where("it.ID = @lookupID", new ObjectParameter("lookupID", ItemData.GetItemData( AllCustomers.SelectedItem))).First(); This statement uses a parameterized Where query builder method to select one of the customer entities by ID. It also calls the First extension method, one of the Queryable methods. 2. Run the program. To test the code, select a customer from the list and then click Delete. If the program prompts you to delete the customer, it successfully located the entity by ID. 284 Microsoft ADO.NET 4 Step by Step Using Query Builder Methods: Visual Basic Note This exercise continues the previous exercise in this chapter. 1. Open the source code view for the CustomerEditor form. Locate the ActDele te_Click event handler; this is a routine that manages the removal of Customer records. Just af- ter the “Locate the customer record” comment (but before the If statement that follows it), add the following code: whichCustomer = Me.ActiveContext.Customers.Where("it.ID = @lookupID", New ObjectParameter("lookupID", ItemData.GetItemData( AllCustomers.SelectedItem))).First() This statement uses a parameterized Where query builder method to select one of the customer entities by ID. It also calls the First extension method, one of the Queryable methods. 2. Run the program. To test the code, select a customer from the list and then click Delete. If the program prompts you to delete the customer, it successfully located the entity by ID. Chapter 16 Understanding Entities Through Objects 285 Summary This chapter introduced Object Services—the core of the Entity Framework—and its concep- tual model-centric way of dealing with data. The ability to modify database-level content through seemingly simple object properties is what EF is all about. There is some work in- volved in getting to that point; work that is thankfully supported by wizards and other tools supplied with Visual Studio. Microsoft added extension methods to both Visual Basic and C# initially to support the then- new LINQ functionality. But beyond this core technology, Microsoft also added ready-to-use methods that bring SQL-query functionality to ordinary object method calls. By stringing a few or even a few dozen of these calls together, you can generate results that match even the most complex hand-crafted SQL database queries. Chapter 16 Quick Reference To Do This Modify a database field through an entity property Create a context instance for the conceptual model. Access an entity from the context. Modify the desired property value by assigning a new value to it. Call the context object’s SaveChanges method. Use a query builder method to sort Customer entities by the FullName field Create a context instance for the conceptual model. Access the collection of entities to be sorted. Call the collection object’s OrderBy extension method, passing it “it.FullName” as the order-by clause. . form. 278 Microsoft ADO. NET 4 Step by Step Note At this point, the Delete button on the CustomerEditor form does not work. The example that appears later in this chapter on page 2 84 adds the. 276 Microsoft ADO. NET 4 Step by Step Note At this point, the Delete button on the CustomerEditor form does not work. The example that appears later in this chapter on page 283 adds. clause. GroupBy GROUP BY Groups results by one or more fields. The method accepts two string arguments: (1) a comma-delimited list of the grouping fields, or what would normally appear in a SQL GROUP BY