ptg 944 CHAPTER 20 Data Access with LINQ to SQL The beautiful thing about generics here is that you don’t have to write the same code to convert a data reader to a generic List for each type. You write the GetListFromCommand() method as a generic method once, and you can use the method with any type that meets the generic constraints in the future. The right way to think of generics is to think of a code template. You can use generics to define a certain pattern of code into which you can plug a particular type. Understanding Lambda Expressions Lambda expressions, another language feature introduced with .NET Framework 3.5, provide you with an extremely terse way of defining methods. Imagine, for example, that you want to programmatically wire up a Click event handler to a button control. Listing 20.6 is an example of one way of doing this. LISTING 20.6 LanguageChanges\NormalMethod.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Init() { btn.Click += new EventHandler(btn_Click); } void btn_Click(object sender, EventArgs e) { lblResult.Text = DateTime.Now.ToString(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head runat=”server”> <title>Normal Method</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Button id=”btn” Text=”Go!” Runat=”server” /> From the Library of Wow! eBook ptg 945 New C# and VB.NET Language Features <asp:Label id=”lblResult” Runat=”server” /> </div> </form> </body> </html> In Listing 20.6, the Page_Init() method associates the Button Click event with the btn_Click() method. When you click the button, the btn_Click() method executes and displays the current date and time. Nothing special here. In .NET Framework 2.0, the notion of anonymous methods for C# was introduced. The advantage of an anonymous method is that you can declare it inline. For example, Listing 20.7 does the same thing as the previous page, except for that it uses an anonymous method to handle the Button Click event. LISTING 20.7 LanguageChanges\AnonymousMethod.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Init() { btn.Click += delegate(object sender, EventArgs e) { lblResult.Text = DateTime.Now.ToString(); }; } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head id=”Head1” runat=”server”> <title>Anonymous Method</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Button id=”btn” 20 From the Library of Wow! eBook ptg 946 CHAPTER 20 Data Access with LINQ to SQL Text=”Go!” Runat=”server” /> <asp:Label id=”lblResult” Runat=”server” /> </div> </form> </body> </html> In Listing 20.7, the Click event is handled with a function declared within the Page_Init() method. NOTE Anonymous methods are not supported by VB.NET, but VB.NET does support lambda expressions—so don’t stop reading if you use VB.NET. Lambda expressions take the notion of the anonymous method one step further. Lambda expressions reduce the amount of syntax required to define a method to its semantic minimum. Listing 20.8 does the same thing as the previous two listings, except that the page uses a lambda expression. LISTING 20.8 LanguageChanges\LambdaExpression.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_Init() { btn.Click += (sender, e) => lblResult.Text = DateTime.Now.ToString(); } </script> <html xmlns=”http://www.w3.org/1999/xhtml”> <head id=”Head1” runat=”server”> From the Library of Wow! eBook ptg 947 New C# and VB.NET Language Features <title>Lambda Expressions</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:Button id=”btn” Text=”Go!” Runat=”server” /> <asp:Label id=”lblResult” Runat=”server” /> </div> </form> </body> </html> The lambda expression in Listing 20.8 is the one that looks like this: (sender, e) => lblResult.Text = DateTime.Now.ToString(); This is just a terse way of writing a method. A lambda expression uses the => operator (the “goes into” operator) to separate a list of method parameters from the method body. The compiler (usually) can infer the data types of the parameters. However, if you want, you can be explicit about the parameter types, like this: (object sender, EventArgs e) => lblResult.Text = DateTime.Now.ToString(); It is also worth mentioning that the parentheses around the parameters are optional when there is a single parameter. So, a lambda expression can be terse. Visual Basic also supports lambda expressions, but in a more limited way. A lambda expression in Visual Basic cannot contain statements; it can only contain expressions. Here’s the syntax in VB for creating a lambda expression: Dim AddNumbers = Function(x, y) x + y Response.Write(AddNumbers(5, 6)) The first statement creates a variable named AddNumbers that represents a lambda expres- sion. The VB syntax Function(x,y) x + y is equivalent to the C# syntax (x,y) => x + y. Next, the lambda function is called with two arguments. 20 From the Library of Wow! eBook ptg 948 CHAPTER 20 Data Access with LINQ to SQL Understanding Extension Methods The idea behind extension methods should also be familiar to anyone who has worked with JavaScript (think prototype). By taking advantage of extension methods, you can add new methods to existing classes. For example, you can make up any method you want and add the method to the String class. I’m constantly HTML-encoding strings because I am paranoid about JavaScript injection attacks. In .NET Framework 2.0, you HTML-encode a string by calling the Server.HtmlEncode() static method, like this: string evilString = “<script>alert(‘boom!’)<” + “/script>”; ltlMessage.Text = Server.HtmlEncode(evilString); In this statement, the static HtmlEncode() method is called on the Server class. Wouldn’t it be nice if we could just call HtmlEncode() on a string directly like this: string evilString = “<script>alert(‘boom!’)<” + “/script>”; ltlMessage.Text = evilString.HtmlEncode(); Using extension methods, we can do exactly that. We can add any methods to a class that we feel like. You create an extension method by creating a static class and declaring a static method that has a special first parameter. Listing 20.9 demonstrates how you create an extension method to add the HtmlEncode() method to the String class. LISTING 20.9 LanguageChanges\MyExtensions.cs public static class MyExtensions { public static string HtmlEncode(this string str) { return System.Web.HttpUtility.HtmlEncode(str); } } The one and only parameter for the HtmlEncode() method is preceded by the keyword this. The parameter indicates the type that the extension method applies to. Creating extension methods in VB.NET is similar to creating extension methods in C#. Listing 20.10 contains the same HtmlEncode() method as the previous listing. LISTING 20.10 LanguageChanges\MyExtensions.cs public static class MyExtensions { public static string HtmlEncode(this string str) { From the Library of Wow! eBook ptg 949 New C# and VB.NET Language Features return System.Web.HttpUtility.HtmlEncode(str); } } When working with VB.NET, you must declare an extension method in a module. Furthermore, you mark the extension methods with the System.Runtime.CompilerServices.Extension attribute. Understanding LINQ Finally, we get to the topic of LINQ—the last topic we need to examine before we can dive into the true subject of this chapter: LINQ to SQL. LINQ stands for Language Integrated Query and consists of a set of new language features added to both the C# and VB.NET languages that enable you to perform queries. LINQ enables you to use SQL query-like syntax within C# or VB.NET. Here’s a simple example of a LINQ query: var words = new List<string> {“zephyr”, “apple”, “azure”}; var results = from w in words where w.Contains(“z”) select w; The first statement creates a generic List of three strings named “words.” The second state- ment is the LINQ query. The LINQ query resembles a backward SQL statement. It retrieves all the words from the List that contain the letter z. After you execute the query, the results variable contains the following list of two words: zephyr azure You can perform a standard LINQ query against any object that implements the IEnumerable<T> interface interface>. An object that implements this interface is called a sequence. Notable examples of sequences are both the generic List class and the standard Array class. (So anything you can dump into an array, you can query with LINQ.) The C# language supports the following clauses that you can use in a query: . from—Enables you to specify the data source and a variable for iterating over the data source (a range variable). . where—Enables you to filter the results of a query. . select—Enables you to specify the items included in the results of the query. . group—Enables you to group related values by a common key. . into—Enables you to store the results of a group or join into a temporary variable. 20 From the Library of Wow! eBook ptg 950 CHAPTER 20 Data Access with LINQ to SQL . orderby—Enables you to order query results in ascending or descending order. . join—Enables you to join two data sources using a common key. . let—Enables you to create a temporary variable to represent subquery results. Building a LINQ query is like building a backward SQL query. You start by specifying a from clause that indicates where you want to get your data. Next, optionally, you specify a where clause that filters your data. Finally, you specify a select clause that gives shape to your data (determines the objects and properties you want to return). Under the covers, standard LINQ queries are translated into method calls on the System.LINQ.Enumerable class. The Enumerable class contains extension methods applied to any class that implements the IEnumerable<T> interface interface>. So, the query var results = from w in words where w.Contains(“z”) select w; is translated into this query by the C# compiler: var results = words.Where( w => w.Contains(“z”) ).Select( w => w ); The first query uses query syntax, and the second query uses method syntax. The two queries are otherwise identical. The query using method syntax accepts lambda expressions for its Where() and Select() methods. The lambda expression used with the Where() method filters the results so that only words that contain the letter z are returned. The Select() method indicates the object and property to return. If we had passed the lambda expression w => w.Length to the Select() method, the query would return the length of each word instead of the word itself. The choice of whether to use query or method syntax when building LINQ queries is purely a matter of preference. Query syntax uses language-specific syntax (C# or VB.NET). Method syntax is language-independent. I find that I use method syntax more than query syntax because query syntax is a subset of method syntax. In other words, you can do more with method syntax. That said, in some cases, writing a query in method syntax is just too verbose. For example, writing left outer joins with LINQ to SQL is much easier using query syntax than method syntax. At the end of the day, the choice of whether to use method or query syntax doesn’t actu- ally matter because all the query syntax statements get translated by the compiler into method syntax. In the case of standard LINQ, those method calls are calls on methods of the Enumerable class. From the Library of Wow! eBook ptg 951 Creating LINQ to SQL Entities Look up the System.Linq.Enumerable class in the SDK documentation to view the full list of methods that the Enumerable class supports. Here is a list of some of the more interest- ing and useful methods: . Aggregate()—Enables you to apply a function to every item in a sequence. . Average()—Returns the average value of every item in a sequence. . Count()—Returns the count of items from a sequence. . Distinct()—Returns distinct items from a sequence. . Max()—Returns the maximum value from a sequence. . Min()—Returns the minimum value from a sequence. . Select()—Returns certain items or properties from a sequence. . Single()—Returns a single value from a sequence. . Skip()—Enables you to skip a certain number of items in a sequence and return the remaining elements. . Take()—Enables you to return a certain number of elements from a sequence. . Where()—Enables you to filter the elements in a sequence. In this section, we’ve been discussing standard LINQ (also called LINQ to Objects). LINQ uses the provider model. There are many different implementations of LINQ, including LINQ to SQL, LINQ to XML, LINQ over DataSets, and LINQ to Entities. There are also third-party implementations of LINQ, including LINQ to NHibernate and LINQ to SharePoint. You can use each of these different flavors of LINQ to query different types of data sources, such as XML files, SharePoint lists, and so on. In this chapter, we are interested in LINQ to SQL because this is the Microsoft version of LINQ designed exclusively for working with database data. So LINQ to SQL is the subject to which we turn now. Creating LINQ to SQL Entities LINQ to SQL enables you to perform LINQ queries against database data. Currently, you can use LINQ to SQL with Microsoft SQL Server 2000, Microsoft SQL Server 2005, or Microsoft SQL Server 2008 (including the SQL Server Express editions). Other databases— such as Oracle, DB2, and Access databases—might be supported in the future, but they are not right now. 20 From the Library of Wow! eBook ptg 952 CHAPTER 20 Data Access with LINQ to SQL NOTE To us e LINQ to SQL, you need to a dd a refere nce to the System.Data.Linq.dll as sembly. Select Website, Add Reference and, beneath the .NET tab, select System.Data.Linq. Performing this action adds a new assembly reference to the <assemblies> section of your web.config file. If you use the LINQ to SQL Designer, this reference is added automatically. In this section, you learn how to create LINQ to SQL entities. An entity is a C# or VB.NET class that represents a database table (or view). You can use a set of standard custom attrib- utes to map classes and properties to tables and columns. You learn how to create entities both by hand and by using the LINQ to SQL Designer. Building Entities by Hand Before you can start performing queries using LINQ to SQL, you need to create one or more entity classes that represent the data you are querying. In this section, you learn how to code these classes by hand. Imagine that you have the following database table named Movie that you want to perform queries against: Movie Column Name Data Type Is Identity? Id Int TRUE Title NVarchar(100) FALSE Director NVarchar FALSE DateReleased DateTime FALSE BoxOfficeTotals Money FALSE You can use the class in Listing 20.11 to represent this table. LISTING 20.11 Entities\App_Code\Movie.cs using System; using System.Data.Linq.Mapping; [Table] public class Movie { [Column(IsPrimaryKey=true, IsDbGenerated=true)] public int Id { get; set; } From the Library of Wow! eBook ptg 953 Creating LINQ to SQL Entities [Column] public string Title { get; set; } [Column] public string Director { get; set; } [Column] public DateTime DateReleased { get; set; } [Column] public decimal BoxOfficeTotals { get; set; } } The class in Listing 20.11 contains a property that corresponds to each column in the Movie database table. Each property is decorated with a custom attribute named Column. This attribute marks the property as one that represents a database column. NOTE The Column and Table attribute classes live in the System.Data.Linq.Mapping namespace. Furthermore, the class itself is decorated with a Table attribute. This attribute marks the class as representing a database table. The Column attribute supports the following properties: . AutoSync—Indicates whether the value of the property is synchronized with the value of the database column automatically. Possible values are OnInsert, Always, and None. . CanBeNull—Indicates whether the property can represent a null value. . DbType—Indicates the database column data type. . Expression—Indicates the expression used by a computed database column. . IsDbGenerated—Indicates that the value of the property is generated in the database (for example, an identity column). . IsDiscriminator—Indicates whether the property holds the discriminator value for an inheritance hierarchy. . IsPrimaryKey—Indicates whether the property represents a primary key column. . IsVersion—Indicates whether the property represents a column that represents a row version (for example, a timestamp column). . Name—Indicates the name of the database column that corresponds to the property. 20 From the Library of Wow! eBook . within the Page_Init() method. NOTE Anonymous methods are not supported by VB .NET, but VB .NET does support lambda expressions—so don’t stop reading if you use VB .NET. Lambda expressions take. the page uses a lambda expression. LISTING 20.8 LanguageChangesLambdaExpression.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC -/ /W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>. the property holds the discriminator value for an inheritance hierarchy. . IsPrimaryKey—Indicates whether the property represents a primary key column. . IsVersion—Indicates whether the property