Programming Visual Basic 2008 phần 7 ppsx

79 300 0
Programming Visual Basic 2008 phần 7 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Basic Query Expressions | 453 Library.Add(New Book With _ {.Title = "Ben-Hur", _ .AuthorID = "LW", .Pages = 544}) Library.Add(New Book With _ {.Title = "Peter Pan", _ .AuthorID = "JB", .Pages = 192}) To make our understanding of the output for each query easier, let’s pretend that I’ve written a method that displays the results of any query in table form. I’ll call the routine ShowResults. Basic Query Expressions LINQ expressions are built from query clauses that have the same feel as clauses in SQL statements at the database level. With the exception of the From clause, which must appear first, the other clauses can generally appear in any order within the query. The From Clause Every basic LINQ query begins with the From keyword. Dim bookBag = From bk In Library ShowResults(bookBag) ' Results > War and Peace LT 1424 ' Anna Karenina LT 976 ' Ben-Hur LW 544 ' Peter Pan JB 192 This four-word query is pretty much the shortest LINQ query you can write. I stored the results of the query in the bookBag variable (with its data type inferred by the query), but the query can also be used directly as an expression. ShowResults(From bk In Library) The bk variable included in the query is known as a range variable or iteration vari- able. (You don’t have to use “bk”; I just chose that name at random. It’s a variable, so give it any name you wish.) This variable provides a way to identify objects and object members from the source data within the query. Since Library is a collection, it wouldn’t make sense to say Library.Title when referring to the title of just one book. Instead, you refer to bk.Title. Personally, I find this variable In source syntax a little indirect. I much prefer the table-alias syntax used in SQL queries. SELECT * FROM Library AS bk The SQL AS keyword performs much the same function as the LINQ In keyword. Yet despite my internal tension, the In syntax prevails; you cannot use the AS syntax in LINQ since the As keyword in Visual Basic is used for data type assignment. 454 | Chapter 17: LINQ The Select Clause If you use only the From clause in your query, it returns all data from the original object set, including all object members. If you want to limit the results so that only some of the members are included, use the Select clause to identify the fields to include. Dim bookBag = From bk In Library _ Select bk.AuthorID, bk.Title ShowResults(bookBag) ' Results > LT War and Peace ' LT Anna Karenina ' LW Ben-Hur ' JB Peter Pan The results set of this new query omits the page count found in the original data. That’s because the LINQ query requested only the AuthorID and Title fields; the Pages member did not make it through the Select clause. Also, notice that I reversed the order of the AuthorID and Title fields from the original class definition. This reversal is reflected in the printed results. Behind the scenes, LINQ is creating a new anonymous type that includes two mem- bers: a string AuthorID field and a string Title field. One instance of this anonymous type is created for each resultant query record. These instances are then bundled up in a new collection that is based on IEnumerable(Of T). This lets you use the query results in a new query, or in any code that would normally interact with a collection of results, such as a For Each statement. Dim bookBag = From bk In Library _ Select bk.AuthorID, bk.Title For Each oneResult In bookBag MsgBox(oneResult.Title) Next oneResult ' The Loop Displays > War and Peace ' Anna Karenina ' Ben-Hur ' Peter Pan In addition to migrating fields from the original objects over to the results set, you can use operators and functions to modify the results. This next example uses the StrReverse function to alter the title name before compiling the results: Dim backward = From bk In Library _ Select StrReverse(bk.Title) ShowResults(backward) ' Results > ecaeP dna raW ' anineraK annA ' ruH-neB ' naP reteP Basic Query Expressions | 455 Although we’re still pretty early into our discussion of LINQ, you should know now that working with LINQ requires lots of experimen- tation. Despite its goal of consistency, LINQ is full of surprises. For instance, the previous example didn’t create the anonymous type col- lection that I expected. Instead, it discerned that the results set con- tained only strings, and created a simple string set instead of a collection of types with a string member. Be on your guard against lit- tle shocks like this when writing LINQ queries. The Distinct Clause By default, the Select clause returns all records from the source. Getting complete information is a good thing, but sometimes it’s too much of a good thing, especially when the information contains duplicates. For instance, this query returns just the author IDs for each available book: Dim justIDs = From bk In Library _ Select bk.AuthorID ShowResults(justIDs) ' Results > LT ' LT ' LW ' JB The results are complete, but “LT” appeared twice. Depending on your needs, that might be a bad thing. By adding the Distinct clause, you can weed out the unneeded duplication. Dim justIDs = From bk In Library _ Select bk.AuthorID _ Distinct ShowResults(justIDs) ' Results > LT ' LW ' JB The Distinct keyword looks at entire records for duplicates. A record is excluded only if all fields in that record exactly match all fields in another record. The Where Clause Whereas the Select clause lets you weed out unwanted fields, the Where clause lets you eliminate entire objects based on criteria you specify. Dim bigBooks = From bk In Library _ Where bk.Pages >= 1000 ShowResults(bigBooks) ' Results > War and Peace LT 1424 456 | Chapter 17: LINQ This query examines all incoming source records in the Library collection and includes a source object in the results only if it has a page count of 1,000 or more. Where clauses can be complex, with multiple criteria joined with And and Or key- words, and grouped with parentheses. Dim choices = From bk In Library _ Where bk.Pages >= 1000 _ Or (bk.Pages < 1000 _ And InStr(bk.Title, "-") > 0) _ Select bk.Title ShowResults(bigBooks) ' Results > War and Peace ' Ben-Hur That last query also showed how you can include non-LINQ features, such as the InStr function, in your criteria, allowing you to restrict the results based on calcu- lated results. The Order By Clause LINQ results, depending on the source of the data, are not guaranteed to appear in any particular order. To generate query results in a specific order, use the Order By clause. The Order By keywords precede one or more source fields or calculated val- ues, delimited by commas, and you can optionally include the Ascending or Descending keyword to reverse the sort order of each sorting field. (Ascending is the default for each field.) Dim bookBag = From bk In Library _ Select bk.Pages, bk.Title _ Order By Pages Descending ShowResults(bookBag) ' Results > 1424 War and Peace ' 976 Anna Karenina ' 544 Ben-Hur ' 192 Peter Pan Fields included in the Order By clause must be present in the Select clause; leave off the range variable prefix ( bk in this case). If you use a From clause without a Select clause, you must include the range variable prefix in your Order By fields. Joining Sources If you were only ever going to query data from a single data collection or source, you probably would not have needed something like LINQ in the first place. When it does come time for you to merge results from different tables, LINQ again provides a SQL-like syntax for joining the tables. Actually, it provides two variations, parallel- ing the syntax variations supported by different SQL vendors. Basic Query Expressions | 457 The first syntax uses the Join keyword to specify a field-specific link. The following query “inner joins” the Library and Writers tables at the expected AuthorID connec- tion point. Dim bookBag = From bk In Library _ Join au In Writers _ On bk.AuthorID Equals au.AuthorID _ Select bk.Title, au.FullName _ Order By bk.Title ShowResults(bookBag) ' Results > Anna Karenina Tolstoy, Leo ' Ben-Hur Wallace, Lew ' Peter Pan Barrie, J. M. ' War and Peace Tolstoy, Leo The special On and Equals keywords assist in the join syntax. If your join involves multiple keys, you can use the And keyword to specify the different key links. Dim results = From t1 In Table1 _ Join t2 In Table2 _ On t1.Key1 Equals t2.Key1 _ And t1.Key2 Equals t2.Key2 The second join syntax lets you use the Where clause to indicate the field links. Dim bookBag = From bk In Library, _ au In Writers _ Where bk.AuthorID = au.AuthorID _ Select bk.Title, au.FullName _ Order By bk.Title ' Same results as before LINQ includes another join variation that generates hierarchical query results.In such queries, one of the fields in each resultant record will be a collection that con- tains multiple results. This syntax allows LINQ to return a list of all authors, one author per row, where each author record includes a “books” field, possibly with multiple values. Dim authorBooks = From au In Writers _ Group Join bk In Library _ On au.AuthorID Equals bk.AuthorID _ Into Published = Group _ Select au.FullName, Published _ Order By FullName ShowResults(authorBooks) ' Results > Barrie, J. M. Peter Pan ' Tolstoy, Leo War and Peace ' Anna Karenina Wallace, Lew Ben-Hur This query has a somewhat strange syntax, but it successfully creates a results set with two columns: FullName (for the author name) and Published (for the collection of books published by a specific author). For each returned record, the Published member is a subordinate collection that can be processed like any other collection. 458 | Chapter 17: LINQ Skip and Take The Skip clause lets you skip over the first x records in the results set, effectively throwing them in the trash, like unwanted banana peels. The Take clause does just the opposite, keeping only the first few records in the generated results. The follow- ing query skips over the first two records in the original data collection, returning just those records following the ignored values: Dim someBooks = From bk In Library _ Select bk.AuthorID, bk.Title _ Skip 2 ShowResults(someBooks) ' Results > LW Ben-Hur ' JB Peter Pan Related Skip While and Take While clauses let you use a Boolean expression instead of a number to indicate when to continue skipping or taking records. Skip and Take are useful for paging results, as when showing just one “page” of results at a time from a larger set of queried results. Logic similar to the following could be used to show just the records destined for CurrentPage: Dim onePageWorth = From bk In Library _ Select bk.AuthorID, bk.Title _ Skip ItemsPerPage * CurrentPage _ Take ItemsPerPage One word of warning about Skip and Take: it does make a difference where you put them in your query. (I’ll explain the technical reason why this is in the “Deferred Execution” section, later in this chapter.) For instance, consider this query based on our original book data: Dim someBooks = From bk In Library _ Order By bk.Title _ Take 2 This query returns Anna Karenina followed by Ben-Hur, as you would expect. But if you move the Take clause earlier, you get a different result. Dim someBooks = From bk In Library _ Take 2 _ Order By bk.Title This time, the query returns Anna Karenina followed by War and Peace. In the first query, the contents of Library were sorted by Title before the two records were taken. In the second query, the two records were taken first, before any sorting had been applied. It’s not just Take and Skip that are impacted by this ordering. All clauses in your query are affected. Thinking through the logic of your query is essential, since a mis- placed clause can give you unexpected results. Aggregate Queries | 459 Converting Results to Other Forms Because the results of any LINQ query conform to the IEnumerable(Of T) interface, they are immediately ready to be used in other queries or in enumerable scans. If you need to access the records in a more traditional form for other purposes, LINQ pro- vides a few conversion features that quickly move the results into either an array or a generic collection. Each query result includes three methods that perform these conversions: ToArray, ToDictionary, and ToList. ToArray converts the results into a standard Visual Basic array, with one result record stored in each array element. Dim queryResults = From Dim arrayVersion = queryResults.ToArray( ) ToList performs a similar operation, creating a new Generic.List collection based on the results of the query. ToDictionary creates a Generic.Dictionary collection, but you must provide a function to ToDictionary that extracts the key. In most cases, a lambda expression that identifies the key field will suffice. Dim authors = From au In Writers _ Order By au.FullName Dim authorDict = authors.ToDictionary(Function(x) x.AuthorID) MsgBox(authorDict("LW").FullName) ' Results > Wallace, Lew Aggregate Queries Aggregate queries let you “sum up” information from a larger query into a con- densed or single result. Instead of starting with the From keyword, pure aggregate queries begin with the Aggregate keyword. Each aggregate query uses one or more of the aggregate functions, such as the Sum function in the following query: Dim numBooks = Aggregate bk In Library _ Into Sum(bk.Pages) MsgBox(numBooks) ' Displays: 3136 LINQ includes eight standard aggregate functions, shown in Table 17-1. Each func- tion accepts an expression that indicates what should be aggregated during the query. Table 17-1. Standard aggregate functions Function Description All Returns a Boolean value indicating whether the expression passed to it is true for all records. The clause All(bk.Pages > 1000) would return False since only one book has more than 1,000 pages. Any Similar to All, but returns True if just one of the records matches the supplied criteria expression. 460 | Chapter 17: LINQ If you include more than one aggregate function in the query, the results set is a single record that includes multiple named fields. Use an alias before the aggregate function to give it a name. (Aliases are allowed in all query types, not just aggregates.) Dim numBooks = Aggregate bk In Library _ Into TotalPages = Sum(bk.Pages), _ AvgPages = Average(bk.Pages) MsgBox(numBooks.AvgPages) ' Displays: 784 You can also include aggregate expressions in standard non-aggregate queries. The following query returns a count of books written by each author, using the Count aggregate function to add up the results for each author: Dim authorBooks = From au In Writers _ Group Join bk In Library _ On au.AuthorID Equals bk.AuthorID _ Into NumBooks = Count(True) _ Select au.FullName, NumBooks _ Order By FullName ShowResults(authorBooks) ' Results > Barrie, J. M. 1 ' Tolstoy, Leo 2 ' Wallace, Lew 1 Advanced Query Expressions You’ve probably been wondering when some of the new technology features such as lambda expressions and extension methods will show up in my examples. Well, in a way, they already did. When you create LINQ queries using query expressions, the Visual Basic compiler converts what you type into code that uses extension methods and lambda expressions. You might remember from Chapter 9 that lambda expres- sions are themselves updated by the compiler into something simpler. Once your queries are broken down into subatomic particles, the CPU is ready to act. But you don’t have to start with full queries. You can create your own queries using extension methods and lambda expressions. The extended methods in question are Average Returns the average of whatever expression is passed to it. Count Returns a count of records with True expression results. To return a count of all records in a query, use Count(True). LongCount Same as Count, but returns a Long instead of an Integer. Max Returns the maximum numeric expression from the set of records. Min Returns the minimum numeric expression from the set of records. Sum Returns the sum of the numeric expressions from the set of records. Table 17-1. Standard aggregate functions (continued) Function Description LINQ to XML | 461 attached to the IEnumerable interface. This means that anything that looks like a col- lection or array can be involved in extension-method-based queries, using lambda expressions as the arguments. Let’s convert one of our earlier queries into its extension method counterpart. Dim bigBooks = From bk In Library _ Where bk.Pages >= 1000 It’s the query that returns only big books. The same query using extension methods looks like this: Dim bigBooks = Library.Where(Function(bk) bk.Pages >= 1000) In this example, the Where method is actually an extension method of the IEnumerable interface, which also includes Select, OrderBy, Join, GroupJoin, Count, Max, Min, and other methods that correspond to operators within the LINQ query language. As I discussed in Chapter 12, you can add your own extension methods to the IEnumerable interface, giving you even more ways to customize your LINQ queries. LINQ to XML In Chapter 13, I introduced XML Literals, XML content that is embedded right into your Visual Basic source code. When you bring LINQ into the picture, you suddenly have a way to generate large XML documents by merging a set of records with an XML Literal template. The following block of code creates an XML document using our Library and Writers collections, intermixing LINQ and XML in a way that actually makes my head hurt: Dim bookXML As XDocument = _ <?xml version="1.0"?> <booklist> <%= From bk In Library _ Join au In Writers _ On bk.AuthorID Equals au.AuthorID _ Order By bk.Title _ Select _ <book> <title><%= bk.Title %></title> <author><%= au.FullName %></author> <pages><%= bk.Pages %></pages> </book> _ %> </booklist> bookXML.Save("books.xml") Notice how you must put line continuation characters in the LINQ portions of the code, but not in the XML portion? Yeah, I hate it, too. But it does generate nice 462 | Chapter 17: LINQ XML. If you look at the books.xml file generated by this code, it contains success- fully merged content from the XML and our original collections. It’s also nicely indented. <?xml version="1.0" encoding="utf-8"?> <booklist> <book> <title>Anna Karenina</title> <author>Tolstoy, Leo</author> <pages>976</pages> </book> <book> <title>Ben-Hur</title> <author>Wallace, Lew</author> <pages>544</pages> </book> <book> <title>Peter Pan</title> <author>Barrie, J. M.</author> <pages>192</pages> </book> <book> <title>War and Peace</title> <author>Tolstoy, Leo</author> <pages>1424</pages> </book> </booklist> The key to intermixing XML and LINQ is correctly placing the <%= and %> markers around the LINQ-specific code. If you look at the sample carefully, you will see that there are two sets of markers, one inside the other. <%= From <title><%= bk.Title %></title> %> The outer set of markers surrounds the entire LINQ query, whereas each inner set of markers identifies a replacement variable to include in the XML content. As easy as it is to generate XML using LINQ, it’s just as easy to query data from existing XML documents. Reloading the XML we just saved allows us to query a list of book titles by intermixing LINQ with XML axis properties. Dim bookXML As XDocument = _ XDocument.Load("books.xml") Dim fromXML = From bx In bookXML <book> _ Select bx.<title>.Value ShowResults(fromXML) ' Results > Anna Karenina ' Ben-Hur ' Peter Pan ' War and Peace [...]... Designer Sooner or later, an image of the table should appear on-screen (see Figure 17- 3) Figure 17- 3 The familiar Activity table and its fields (properties) 466 | Chapter 17: LINQ 3 Build your application I’ve found that this step is necessary in some installs of Visual Studio, but not in others It refreshes Visual Basic s view of the new LibraryDataContext classes To build the application, select... new set of matching items displayed For instance, if you are viewing the details of an award-winning (one can hope) Visual Basic 2008 programming book and click on the publisher name for that item, the PanelItems panel appears, listing all items made by that publisher 470 | Chapter 17: LINQ The form also includes a set of Back buttons (in the upper-left corner) that work like the Back button in your... help you build the entities from the source database structures One key tool is the ADO.NET Entity Data Model Designer, a visual drag-and-drop tool that makes creating entities as easy as creating Visual Basic forms Because the ADO.NET Entity Framework comes out after Visual Studio 2008, I will not be demonstrating the framework in this book LINQ to DataSet LINQ supports queries of records within ADO.NET... (see Figure 17- 1) Figure 17- 1 Adding a new dbml class A new Library.dbml item appears in your project, which opens the O/R Designer, shown in Figure 17- 2 If you examine its properties, you’ll see that its name is LibraryDataContext LINQ for ADO.NET-Related Data | 465 Figure 17- 2 The O/R Designer; not much to look at right now 2 Add tables to the O/R Designer Open the Server Explorer in Visual Studio... and even update records in the actual database tables You can examine these generated queries using the SQL Query Debug Visualizer tool It doesn’t come with Visual Studio, but you can download it from Microsoft’s MSDN web site Deferred Execution When you build a LINQ query, Visual Basic does not process the query immediately Instead, it defers execution, running the query only when you request a record... that looks something like Figure 17- 5 Despite working so hard to ensure that all the names avoid conflicts and that they are singular, when we use the library data context in our LINQ queries, we’ll find that all of the class names for these LINQ-to-SQL-generated tables are somehow pluralized (QPublishers instead of QPublisher) Amazing 472 | Chapter 17: LINQ Figure 17- 5 Why aren’t they standing in a... library items by patrons PROJECT ACCESS Load the Chapter 17 (Before) Code project, either through the New Project templates or by accessing the project directly from the installation directory To see the code in its final form, load Chapter 17 (After) Code instead Looking Up Library Items When we built the main Library form back in Chapter 7, we included fields that allowed a patron to search for library... that comes with Visual Studio Using LINQ to SQL is done in five easy steps You can follow along in a new Windows Forms project if you want: 1 Add a new “dbml” file This file—actually, a few files that Visual Studio displays as one—describes your data context, the master class that contains link code for each database table that you will use in your application To create this file from a Visual Studio... was called in Beta 1 ADO.NET and LINQ now work very well together In fact, ADO.NET sports three LINQ providers LINQ to Entities Soon after the launch of Visual Studio 2008, Microsoft released the ADO.NET Entity Framework This interface between your programming code and a database will let you define a logical view of your system For instance, you can create an entity called Order that includes your... Instead of just being a simple query, it’s a complex query that is built up little by little The query starts with the basics, requesting matching records from the database’s NamedItem table (The libraryDC variable is the opened data context for the Library database.) 474 | Chapter 17: LINQ Dim itemQuery = From ni In libraryDC.QNamedItems Next, if the user requested items of a specific media type (“just . Model Designer, a visual drag-and-drop tool that makes creating entities as easy as creating Visual Basic forms. Because the ADO.NET Entity Framework comes out after Visual Studio 2008, I will not. (one can hope) Visual Basic 2008 programming book and click on the publisher name for that item, the PanelItems panel appears, listing all items made by that publisher. Project | 471 The form also. Query Debug Visualizer tool. It doesn’t come with Visual Studio, but you can down- load it from Microsoft’s MSDN web site. Deferred Execution When you build a LINQ query, Visual Basic does not

Ngày đăng: 13/08/2014, 08:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan