A lambda expression uses the => operator the “goes into” operator to separate a list of method parameters from the method body.. In .NET Framework 2.0, you HTML-encode a string by callin
Trang 1The 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” />
Trang 2<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”
Trang 3Text=”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”>
Trang 4<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
Trang 5Understanding 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)
{
Trang 6return 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 statestate-ment 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
Trang 7orderby—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
Trang 8Look 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
Trang 9NOTE
To use LINQ to SQL, you need to add a reference to the System.Data.Linq.dll assembly
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?
Title NVarchar(100) 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; }
Trang 10[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