Tải bản đầy đủ (.pdf) (10 trang)

Professional ASP.NET 3.5 in C# and Visual Basic Part 52 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (279.41 KB, 10 trang )

Evjen c09.tex V2 - 01/28/2008 2:09pm Page 466
Chapter 9: Querying with LINQ
Next, the code uses a very simple LINQ query to select all of the Movie objects from the generic movies
collection. Notice that this specific LINQ query utilizes new language keywords like
from
and
select
in the query statement. These syntax additions are first class members of the .NET languages, therefore
Visual Studio 2008 can offer you development assistance such as strong type checking and IntelliSense,
making it easier for you to find and fix problems in your code.
The query also defines a new variable
m
. This variable is used in two ways in the query. First, by defining
it in the
from
statement from
m
, we are telling LINQ to make
m
represent the individual collection item,
whichinthiscaseisa
Movie
object. Telling LINQ this enables it to understand the structure of the objects
we are querying, and as you will see later, also gives us IntelliSense to help create the query.
The second use of
m
in the query is in the
select
statement. Using
m
in the


select
statement tells LINQ
to output a projection that matches the structure of
m
. In this case that means LINQ creates a projection
that matches the
Movie
object structure.
We could just have easily created our own custom projection by explicitly defining the fields we wanted
returned from the query using the new keyword along with the select operator. This is shown below in
Listing 9-7.
Listing 9-7: Creating a c ustom projection with LINQ
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _
Select New With {m.Title, m.Genre}
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var query = from m in movies
select new { m.Title, m.Genre };
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
Notice that rather than simply selecting

m
, we have defined a new projection containing only the Title
and Genre values.
You can even go so far as to explicitly define the field names that the objects in the resultset will expose.
For example, you may want to more explicitly name the Title and Genre fields to more fully describe
their contents. Using LINQ, it’s easy to do this, as shown in Listing 9-8.
466
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 467
Chapter 9: Querying with LINQ
Listing 9-8: Creating custom projection field names
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _
Select New With {.MovieTitle = m.Title, .MovieGenre = m.Genre}
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var query = from m in movies
select new { MovieTitle = m.Title, MovieGenre = m. Genre };
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
This sample explicitly defined the Fields that will be exposed by the resultset as MovieTitle and
MovieGenre. You can see in Figure 9-1, that because of this change, the column headers in the
GridView have changed to match.

Figure 9-1 Customized GridView column
headers as the result of the LINQ projection
467
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 468
Chapter 9: Querying with LINQ
Finally the code binds the GridView control to the enumerable list of Movie object returned by the LINQ
query.
As shown in Figure 9-2, running the code from Listing 9-6 results in the same vanilla Web page as the
one generated by Listing 9-2.
Figure 9-2 The results of a basic LINQ query bound to a GridView control
LINQ also includes the ability to order the results using the order by statement. As with SQL you can
choose to order the results in either ascending or descending order, as shown in Listing 9-9.
Listing 9-9: Controlling data ordering using LINQ
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _
Order By m.Title Descending _
Select New With {.MovieTitle = m.Title, .MovieGenre = m.Genre}
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
468
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 469
Chapter 9: Querying with LINQ
var query = from m in movies

orderby m.Title descending
select new { MovieTitle = m.Title, MovieGenre = m. Genre };
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
Another great feature of the new LINQ syntax is the dramatic improvement in readability and under-
standability that it makes in your code. LINQ enables you to simply express the intention of your query,
indicating to the compiler what you want your code to do, but leaving it up to the compiler to best
determine how it should be done.
While these new keywords are what enable you to construct LINQ queries using a simple and clear
SQL-like syntax, rest assured there is no magic occurring. These keywords actually map to extension
methods on the Movies collection. You could actually write the same LINQ query directly using these
extension methods and it would look like this:
VB
Dim query = movies.Select( Function(m as Movie) m )
C#
var query = movies.Select(m => m);
This is what the compiler translates the keyword syntax into during its compilation process. You may be
wondering how the
Select
method got added to our generic
List
<
Movies
> collection because if you
look at the object structure of
List
<
T
>, there is no

Select
method. LINQ adds the
Select
method,
and many other methods it uses to the base Enumerable class, using Extension Methods. Therefore, any
class that implements IEnumerable will be extended by LINQ with these methods. You can see all of the
methods added by LINQ by right-clicking on the
Select
method in Visual Studio and choosing the View
Definition option from the context menu. Doing this causes Visual Studio to display the class metadata
for LINQ’s
Enumerable
class. If you scroll through this class, you will see not only
Select
,butother
methods such as
Where
,
Count
,
Min
,
Max
, and many other methods that LINQ automatically adds to
any object that implements the IEnumerable interface.
Delayed Execution
An interesting feature of LINQ is its delayed execution behavior. This means that even though you may
execute the query statements at a specific point in your code, LINQ is smart enough to delay the actual
execution of the query until it is accessed. For example, in the previous samples, although the LINQ
query was written before the binding of the GridView controls, LINQ will not actually execute the query

we have defined until the GridView control begins to enumerate through the query results.
Query Filters
LINQ also supports adding query filters using a familiar SQL-like
where
syntax. You can modify the
LINQ query from Listing 9-3 to add filtering capabilities by adding a
where
clause to the query, as shown
in Listing 9-10.
469
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 470
Chapter 9: Querying with LINQ
Listing 9-10: Adding a filter to a LINQ query
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _
Where m.Genre = 0 _
Select m
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var query = from m in movies
where m.Genre==0
select m;
this.GridView1.DataSource = query;

this.GridView1.DataBind();
}
By adding this simple
where
clause to the LINQ query, the results returned by the query are filtered to
show movies from the 0 genre only, as shown in Figure 9-3.
Figure 9-3 A filtered list of Movies
Also, notice that, because LINQ is a first-class member of .NET, Visual Studio is able to provide an
excellent coding experience as you are constructing your LINQ queries. In this sample, as you enter the
470
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 471
Chapter 9: Querying with LINQ
where
clause, Visual Studio gives you IntelliSense for the possible parameters of
m
(the Movie object), as
shown in Figure 9-4.
Figure 9-4 Because LINQ is a first class language concept, Visual
Studio can give you Intellisense
The
where
clause in LINQ behaves similarly to the SQL
where
clause, enabling you to include sub-queries
and multiple where clauses, as shown in Listing 9-11.
Listing 9-11: Adding a Where clause to a LINQ query
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _

Where m.Genre = 0 And m.Runtime > 92 _
Select m
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var query = from m in movies
where m.Genre == 0 && m.RunTime > 92
select m;
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
In this sample, the
where
clause includes two parameters, one restricting the movie genre, the other
restricting the movie’s runtime.
471
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 472
Chapter 9: Querying with LINQ
Data Grouping
LINQ also greatly simplifies grouping data, again using a SQL-like
group
syntax. To show how easy
LINQ makes this, you can modify the original Listing 9-4 to use a LINQ query. The modified code is
shown in Listing 9-12.
Listing 9-12: Grouping data using a LINQ query
VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim query = From m In movies _
Group By m.Genre Into g = Group, Count()
Me.GridView1.DataSource = query
Me.GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var query = from m in movies
group m by m.Genre into g
select new { Genre = g.Key, Count = g.Count() };
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
This LINQ query uses the
group
keyword to group the movie data by genre. Additionally, because a
group action does not naturally result in any output, the query creates a custom query projection using
the techniques discussed earlier. The results of this query are shown in Figure 9-5.
Figure 9-5 Grouped data results
472
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 473
Chapter 9: Querying with LINQ
Using LINQ to do this allows you to significantly reduce the lines of code required. If we compare the
amount of code required to perform the grouping action in Listing 9-4, with the previous listing using
LINQ, you can see that the number of lines of code has dropped from 18 to 3, and the readability and
clarity of the code has improved.

Other LINQ Operators
Besides basic selection, filtering and grouping, LINQ also includes many operators you can execute on
enumerable objects. Most of these operators are available for you to use and are similar to operators you
find in SQL, such as Count, Min, Max, Average, and Sum, as shown in Listing 9-13.
Listing 9-13: Using LINQ query operators
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Me.TotalMovies.Text = movies.Count.ToString()
Me.LongestRuntime.Text = movies.Max(Function(m) m.Runtime).ToString()
Me.ShortestRuntime.Text = movies.Min(Function(m) m.Runtime).ToString()
Me.AverageRuntime.Text = movies.Average(Function(m) m.Runtime).ToString()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
this.TotalMovies.Text = movies.Count.ToString();
this.LongestRuntime.Text = movies.Max(m => m.RunTime).ToString();
this.ShortestRuntime.Text = movies.Min(m => m.RunTime).ToString();
this.AverageRuntime.Text = movies.Average(m => m.RunTime).ToString();
}
This listing demonstrates the use of the Count, Max, Min, and Average operators with the movies collec-
tion. Notice that for all but the Count operator, you need to provide the method with the specific field
you want to execute the operation on. This is done using a Lambda expression.
LINQ Joins
LINQ also supports the unioning of data from different collections using a familiar SQL-like join syntax.
For example, in our sample data thus far, we have only been able to display the Genre as a numeric ID.
It would be preferable to actually display the name of each Genre instead. To do this, you simply create
a Genre class, which defines the properties of the genre, as shown in Listing 9-14

Listing 9-14: A simple Genre class
VB
Public Class Genre
Private _id As Integer
Private _name As String
Continued
473
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 474
Chapter 9: Querying with LINQ
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
C#
public class Genre
{
public int ID { get; set; }

public string Name { get; set; }
}
Next you can add a
GetGenres
method to your Web page that returns a list of Genre objects, as shown in
Listing 9-15.
Listing 9-15: Populating a collection of Genres
VB
Public Function GetGenres() As List(Of Genre)
Dim genres As Genre() = { _
New Genre With {.ID = 0, .Name = "Comedy"}, _
New Genre With {.ID = 1, .Name = "Drama"}, _
New Genre With {.ID = 2, .Name = "Action"} _
}
Return New List(Of Genre)(genres)
End Function
C#
public List<Genre> GetGenres()
{
return new List<Genre> {
new Genre { ID=0, Name="Comedy" } ,
new Genre { ID=1, Name="Drama" } ,
new Genre { ID=2, Name="Action" }
};
}
474
Evjen c09.tex V2 - 01/28/2008 2:09pm Page 475
Chapter 9: Querying with LINQ
Finally, you can modify the Page Load event, including the LINQ query, to retrieve the Genres list and,
using LINQ, join that to the Movies list. This is shown in Listing 9-16.

Listing 9-16: Joining Genre data with Movie data using a LINQ query
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim genres = GetGenres()
Dim query = From m In movies Join g In genres _
On m.Genre Equals g.ID _
Select New With {.Title = m.Title, .Genre = g.Name}
GridView1.DataSource = query
GridView1.DataBind()
End Sub
C#
protected void Page_Load(object sender, EventArgs e)
{
var movies = GetMovies();
var genres = GetGenres();
var query = from m in movies
join g in genres on m.Genre equals g.ID
select new { m.Title, Genre = g.Name } ;
this.GridView1.DataSource = query;
this.GridView1.DataBind();
}
As you can see in this sample, the join syntax is relatively simple. You tell LINQ to include the genres
object, and then tell LINQ which fields it should associate.
Paging Using LINQ
LINQ also makes it much easier to include paging logic in your Web application by exposing the
Skip
and
Take
methods. The

Skip
method enables you to skip a defined number of records in the resultset.
The
Take
method enables you to specify the number of records to return from the resultset. By calling
Skip
,andthen
Take
, you can return a specific number of records from a specific location of the resultset.
This is shown in Listing 9-17.
Listing 9-17: Simple Paging using LINQ methods
VB
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim movies = GetMovies()
Dim genres = GetGenres()
Dim query = (From m In movies _
Join g In genres On m.Genre Equals g.ID _
Continued
475

×