getting familiar with linq to objects

50
Getting familiar with LINQ to Objects Florin−Tudor Cristea, Microsoft Student Partner

Upload: uzuri

Post on 16-Feb-2016

42 views

Category:

Documents


0 download

DESCRIPTION

Getting familiar with LINQ to Objects. Florin−Tudor Cristea, Microsoft Student Partner. Introducing our running example. “Never trust a computer you can’t throw out a window.” (Steve Wozniak). LinqBooks , a personal book-cataloging system. - PowerPoint PPT Presentation

TRANSCRIPT

Slide 1

Getting familiarwith LINQ to ObjectsFlorinTudor Cristea, Microsoft Student PartnerIntroducing our running exampleNever trust a computer you cant throw out a window. (Steve Wozniak) LinqBooks, a personal book-cataloging system.The main features LinqBooks should have include the ability to:Track what books we have;Store what we think about them;Retrieve more information about our books;Publish our list of books and our review information.The technical features well implement include:Querying/inserting/updating data in a local database;Providing search capabilities over both the local catalog and third parties (such as Amazon or Google);Importing data about books from a web site;Importing and persisting some data from/in XML documents;Creating RSS feeds for the books you recommend.In order to implement these features, well use a set of business entities. The object model well use consists of the following classes: Book, Author, Publisher, Subject, Review, and User. Well first use these objects in memory with LINQ to Objects, but later on well have to persist this data in a database. Using LINQ with in-memory collectionsIf Java had true garbage collection, most programs would delete themselves upon execution. (Robert Sewell) All that is required for a collection to be queryable through LINQ to Objects is that it implements the IEnumerable interface. As a reminder, objects implementing the IEnumerable interface are called sequences in LINQ vocabulary. The good news is that almost every generic collection provided by the .NET Framework implements IEnumerable. This means that youll be able to query the usual collections you were already working with in .NET 2.0.ArraysUntypedArray.csprojTypedArray.csproj

Generic listsSystem.Collections.Generic.ListSystem.Collections.Generic.LinkedListSystem.Collections.Generic.QueueSystem.Collections.Generic.StackSystem.Collections.Generic.HashSetSystem.Collections.ObjectModel.CollectionSystem.ComponentModel.BindingListGenericList.csproj

Generic dictionariesSystem.Collections.Generic.DictionarySystem.Collections.Generic.SortedDictionarySystem.Collections.Generic.SortedListGenericDictionary.csproj

StringString.csprojThe nongeneric collections do not implement IEnumerable, but implement IEnumerable. Does this mean that you wont be able to use LINQ with DataSet or ArrayList objects, for example? Fortunately, solutions exist. Later well demonstrate how you can query nongeneric collections thanks to the Cast and OfType query operators.Here is an overview of the families of the standard query operators: Restriction, Projection, Partitioning, Join, Ordering, Grouping, Set, Conversion, Equality, Element, Generation, Quantifiers, and Aggregation. As you can see, a wide range of operations is supported.Using LINQ with ASP.NET and Windows FormsThere are only two kinds of programming languages: those people always bitch about and those nobody uses. (Bjarne Stroustrup)ASP.NET controls support data binding to any IEnumerable collection. This makes it easy to display the result of language-integrated queries using controls like GridView, DataList, and Repeater.Step1.aspx

We can use two methods to display only the properties we want: either declare specific columns at the grid level, or explicitly select only the Title and Price properties in the query.Step2a.aspx, Step2b.aspxYou can use an anonymous type to map your domain model to a presentation model. In the following query, creating an anonymous type allows a flat view of our domain model:

from book in SampleData.Bookswhere book.Title.Length > 10orderby book.Priceselect new { book.Title, book.Price, Publisher = book.Publisher.Name, Authors = book.Authors.Count() };Using LINQ in a Windows Forms application isnt more difficult than with ASP.NET in a web application. Well see how we can do the same kind of databinding operations between LINQ query results and standard Windows Formscontrols in a sample application.FormStrings.csFormBooks.cs (DataPropertyName)You should notice two things in comparison to the code we used for the ASP.NET web application sample. First, we use an anonymous type to create objects containing a Book property. This is because the DataGridView control displays the properties of objects by default. If we returned strings instead of custom objects, all we would see displayed would be the titles Length, because thats the only property on strings. Second, we convert the result sequence into a list. This is required for the grid to perform data binding. Alternatively, we could use a BindingSource object.Focus on major standard query operatorsThere are two major products that come out of Berkeley: LSD and UNIX. We dont believe this to be a coincidence.(Jeremy S. Anderson)Filtering: Where

public static IEnumerable Where( this IEnumerable source, Func predicate);

public static IEnumerable Where( this IEnumerable source, Func predicate);IEnumerable books = SampleData.Books.Where(book => book.Price >= 15);

IEnumerable books = SampleData.Books.Where( (book, index) => (book.Price >= 15) && ((index & 1) == 1));Projection: Select

public static IEnumerable Select( this IEnumerable source, Func selector);

IEnumerable titles = SampleData.Books.Select(book => book.Title);Projection: SelectMany

public static IEnumerable SelectMany( this IEnumerable source, Func selector);

The SelectMany operator maps each element from the sequence returned by the selector function to a new sequence, and concatenates the results.IEnumerable tmp = SampleData.Books .Select(book => book.Authors);foreach (var authors in tmp){ foreach (Author author in authors) { Console.WriteLine(author.LastName); }}IEnumerable authors = SampleData.Books .SelectMany(book => book.Authors);foreach (Author author in authors){Console.WriteLine(author.LastName);}

from book in SampleData.Booksfrom author in book.Authorsselect author.LastNameThe Select and SelectMany operators can be used to retrieve the index of each element in a sequence. Lets say we want to display the index of each book in our collection before we sort them in alphabetical order (SelectIndex.csproj):

var books = SampleData.Books .Select((book, index) => new { index, book.Title }) .OrderBy(book => book.Title);To remove duplication, we can use the Distinct operator. Distinct eliminates duplicate elements from a sequence. In order to compare the elements, the Distinct operator uses the elements implementation of the IEquatable.Equals method if the elements implement the IEquatable interface. It uses their implementation of theObject.Equals method otherwise (Distinct.csproj).var authors = SampleData.Books .SelectMany(book => book.Authors) .Distinct() .Select(author => author.FirstName + " " + author.LastName);Conversion: ToArray, ToList, ToDictionaryToArray and ToList are useful when you want to request immediate execution of a query or cache the result of a query. When invoked, these operators completely enumerate the source sequence on which they are applied to build an image of the elements returned by this sequence.

Dictionary isbnRef = SampleData.Books.ToDictionary(book => book.Isbn);Book linqRules = isbnRef["0-111-77777-2"];Aggregate: Count, Sum, Min, Maxvar minPrice = SampleData.Books.Min(book => book.Price);var maxPrice = SampleData.Books.Select(book => book.Price).Max();var totalPrice = SampleData.Books.Sum(book => book.Price);var nbCheapBooks = SampleData.Books.Where(book => book.Price < 30).Count();Creating views on an object graph in memory19 Jan 2038 at 3:14:07 AM(End of the word according to Unix2^32 seconds after January 1, 1970)SortingLets say wed like to view our books sorted by publisher, then by descending price, and then by ascending title (Sorting.aspx):

from book in SampleData.Books orderby book.Publisher.Name, book.Price descending, book.Title select new { Publisher = book.Publisher.Name, book.Price, book.Title }; A query expressions orderby clause translates to a composition of calls to the OrderBy, ThenBy, OrderByDescending, and ThenByDescending operators:

SampleData.Books .OrderBy(book => book.Publisher.Name) .ThenByDescending(book => book.Price) .ThenBy(book => book.Title) .Select(book => new { Publisher = book.Publisher.Name, book.Price, book.Title });Nested queriesLets say we want to display publishers and their books in the same grid (Nested.aspx):

from publisher in SampleData.Publishersselect new { Publisher = publisher.Name, Books = from book in SampleData.Books where book.Publisher.Name == publisher.Name select book }GroupingUsing grouping, well get the same result aswith the previous sample except that we dontsee the publishers without books this time (Grouping.aspx):

from book in SampleData.Booksgroup book by book.Publisher into publisherBooksselect new { Publisher = publisherBooks.Key.Name, Books = publisherBooks };The publisherBooks group is an instance of the IGrouping interface. Here is how this interface is defined:

public interface IGrouping : IEnumerable{ TKey Key { get; }}You can see that an object that implements the IGrouping generic interface has a strongly typed key and is a strongly typed enumeration. In our case, the key is a Publisher object, and the enumeration is of type IEnumerable.

Advantages:the query is shorter;we can name the group.from book in SampleData.Booksgroup book by book.Publisher into publisherBooksselect new { Publisher = publisherBooks.Key.Name, Books=publisherBooks, publisherBooks.Count() };Group joinJoin operators allow us to perform the same kind of operations as projections, nested queries, or grouping do, but their advantage is that they follow a syntax close to what SQL offers (Joins.aspx):

from publisher in SampleData.Publishersjoin book in SampleData.Books on publisher equals book.Publisher into publisherBooksselect new { Publisher = publisher.Name, Books = publisherBooks };This is a group join. It bundles each publishers books as sequences named publisherBooks. As with nested queries, publishers with no books appear in the results this time.Inner joinAn inner join essentially finds the intersectionbetween two sequences. With an inner join, theelements from two sequences that meet amatching condition are combined to form asingle sequence:

from publisher in SampleData.Publishersjoin book in SampleData.Books on publisher equals book.Publisherselect new { Publisher=publisher.Name, Book=book.Title };This query is similar to the one we used in the group join sample. The difference here is that we dont use the into keyword to group the elements. Instead, the books are projected on the publishers.

SampleData.Publishers .Join(SampleData.Books, // inner sequence publisher => publisher, // outer key selector book => book.Publisher, // inner key selector (publisher, book) => new { Publisher = publisher.Name, Book = book.Title }); // result selectorLeft outer joinWhen we want to keep all elements from the outer sequence, independently of whether there is a matching element in the inner sequence, we need to perform a left outer join.from publisher in SampleData.Publishersjoin book in SampleData.Books on publisher equals book.Publisher into publisherBooksfrom book in publisherBooks.DefaultIfEmpty()select new { Publisher = publisher.Name, Book = book == default(Book) ? "(no books)" : book.Title};Cross joinA cross join computes the Cartesian product ofall the elements from two sequences.

from publisher in SampleData.Publishersfrom book in SampleData.Booksselect new { Correct = (publisher == book.Publisher), Publisher = publisher.Name, Book = book.Title };SampleData.Publishers.SelectMany( publisher => SampleData.Books.Select( book => new { Correct = (publisher == book.Publisher), Publisher = publisher.Name, Book = book.Title }));PartitioningLets say we want to display a maximum ofthree books on a page. This can be done easilyusing the GridView controls paging features (Paging.aspx):