Next, the code uses a very simple LINQ query to select all of the Movie objects from the generic moviescollection.. Listing 9-7: Creating a custom projection with LINQ VB Protected Sub P
Trang 1Next, 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 likefromandselect
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 variablem This variable is used in two ways in the query First, by defining
it in thefromstatement fromm, we are telling LINQ to makemrepresent the individual collection item,
which in this case is aMovieobject 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 ofmin the query is in theselectstatement Usingmin theselectstatement tells LINQ
to output a projection that matches the structure ofm In this case that means LINQ creates a projection
that matches theMovieobject 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 custom 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 selectingm, 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
Trang 2Listing 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
Trang 3Finally 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();
Trang 4var 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 theSelectmethod got added to our genericList<Movies> collection because if you
look at the object structure ofList<T> , there is noSelectmethod LINQ adds theSelectmethod,
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 theSelectmethod 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’sEnumerableclass If you scroll through this class, you will see not onlySelect, but other
methods such asWhere,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-likewheresyntax You can modify the
LINQ query from Listing 9-3 to add filtering capabilities by adding awhereclause to the query, as shown
in Listing 9-10
469
Trang 5Listing 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 simplewhereclause 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
Trang 6whereclause, Visual Studio gives you IntelliSense for the possible parameters ofm(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
Thewhereclause in LINQ behaves similarly to the SQLwhereclause, 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, thewhereclause includes two parameters, one restricting the movie genre, the other
restricting the movie’s runtime
471
Trang 7Data Grouping
LINQ also greatly simplifies grouping data, again using a SQL-likegroupsyntax 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 thegroupkeyword 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
Trang 8Using 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
Trang 9Public 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 aGetGenresmethod 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" } };
}
Trang 10Finally, 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 theSkip
andTakemethods TheSkipmethod enables you to skip a defined number of records in the resultset
TheTakemethod enables you to specify the number of records to return from the resultset By calling
Skip, and thenTake, 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