11-linq to objects

34
LINQ to Objects Chapter 11 C# Development

Upload: andy-olsen

Post on 07-Apr-2015

156 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 11-LINQ to Objects

LINQ to Objects

Chapter 11

C# Development

Page 2: 11-LINQ to Objects

2

Contents

1. Introduction to LINQ

2. Using LINQ with arrays

3. Using LINQ with collections

4. LINQ under the hood

5. Additional LINQ techniques

Page 3: 11-LINQ to Objects

3

1. Introduction to LINQ

What is LINQ? The purpose of LINQ LINQ variations A few technical details

Page 4: 11-LINQ to Objects

4

What is LINQ?

LINQ = "Language Integrated Query"

Or to put it another way:• A set of C# (VB etc) keywords and .NET types,

methods, etc.• … to query data from various data sources

Page 5: 11-LINQ to Objects

5

The Purpose of LINQ

LINQ provides a unified syntax for querying data• Regardless of where the data is coming from

Better than the pre-LINQ situation, where you have to use completely different query syntaxes• Objects in memory

Use arrays and/or .NET collection classes APIs• Relational data

Use ADO.NET connections, ADO.NET DataSets, etc.• XML

Use DOM, XmlReader, XmlWriter, etc.

Page 6: 11-LINQ to Objects

6

LINQ Variations

There are several LINQ variations currently available:• LINQ to objects• LINQ to XML• LINQ to DataSet• LINQ to Entities

We'll examine all of these techniques in this and coming chapters

Page 7: 11-LINQ to Objects

7

A Few Technical Details

LINQ queries use SQL-like syntax• Via C# keywords such as select, from, where,

etc.• Can manipulate any data source that

implements the IEnumerable<T> generic interface

• LINQ queries are strongly typed

C# / .NET features utilized by LINQ:• Implicitly-typed local variables• Extension methods• Object/collection initialization syntax• Anonymous types• Lambda expressions

Page 8: 11-LINQ to Objects

8

2. Using LINQ with Arrays

Overview Querying arrays without LINQ Querying arrays with LINQ C# LINQ query operators Deferred execution Immediate execution

Page 9: 11-LINQ to Objects

9

Overview

In this section, we'll show how to use LINQ to query data in an array• See the LinqOverArrays project• Automatically references LINQ-related

assemblies

Defines types that represent the core LINQ API

Defines types for LINQ to DataSets

Defines types for LINQ to XML

Page 10: 11-LINQ to Objects

10

Querying Arrays Without LINQ

Here's a traditional array-processing example• Uses manual iteration / conditional logic• Doesn't use LINQprivate static void DemoNonLinq(){ // Here's an array of strings. string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Get the cities that contain a space character. string[] citiesWithSpaces = new string[cities.Length]; for (int i = 0; i < cities.Length; i++) { if (cities[i].Contains(" ")) citiesWithSpaces[i] = cities[i]; }

Console.WriteLine("Cities with spaces (without using LINQ): "); foreach (string s in citiesWithSpaces) { if (s != null) Console.WriteLine("City: {0}", s); }}

private static void DemoNonLinq(){ // Here's an array of strings. string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Get the cities that contain a space character. string[] citiesWithSpaces = new string[cities.Length]; for (int i = 0; i < cities.Length; i++) { if (cities[i].Contains(" ")) citiesWithSpaces[i] = cities[i]; }

Console.WriteLine("Cities with spaces (without using LINQ): "); foreach (string s in citiesWithSpaces) { if (s != null) Console.WriteLine("City: {0}", s); }}

Page 11: 11-LINQ to Objects

11

Querying Arrays With LINQ

Here's the equivalent example using LINQ

• Builds a LINQ query expression using query operators

• Assigns to var (it's IEnumerable<string> actually)

• Query is executed when we enumerate it in the loop

private static void DemoLinq(){ // Here's an array of strings. string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Use a LINQ query expression to get the cities that contain a space character. var subset = from g in cities where g.Contains(" ") orderby g.Length ascending select g;

Console.WriteLine("\nCities with spaces (using LINQ): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

private static void DemoLinq(){ // Here's an array of strings. string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Use a LINQ query expression to get the cities that contain a space character. var subset = from g in cities where g.Contains(" ") orderby g.Length ascending select g;

Console.WriteLine("\nCities with spaces (using LINQ): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

Page 12: 11-LINQ to Objects

12

C# LINQ Query Operators

Here are some of the most commonly used C# LINQ query operators:• from, in• where• select• join, on, equals, into• orderby, ascending, descending• group, by

Page 13: 11-LINQ to Objects

13

Deferred Execution

LINQ queries use a "deferred execution" model• Executed when you enumerate the query, not

when you create it• Therefore, if you enumerate a query several

times, the query will be executed afresh each time

private static void DemoDeferredExecution(){ int[] examMarks = { 62, 75, 85, 59, 96, 80 };

// Get the "A" grades, i.e. >= 70. var subset = from m in examMarks where m >= 70 select m;

Console.WriteLine("\nA-grade exams (LINQ query is executed here):"); foreach (var m in subset) Console.WriteLine("Mark: {0}", m);

// Change some data in the array. examMarks[0] = 99;

Console.WriteLine("\nA-grade exams (LINQ query is executed AGAIN here):"); foreach (var m in subset) Console.WriteLine("Mark: {0}", m);}

private static void DemoDeferredExecution(){ int[] examMarks = { 62, 75, 85, 59, 96, 80 };

// Get the "A" grades, i.e. >= 70. var subset = from m in examMarks where m >= 70 select m;

Console.WriteLine("\nA-grade exams (LINQ query is executed here):"); foreach (var m in subset) Console.WriteLine("Mark: {0}", m);

// Change some data in the array. examMarks[0] = 99;

Console.WriteLine("\nA-grade exams (LINQ query is executed AGAIN here):"); foreach (var m in subset) Console.WriteLine("Mark: {0}", m);}

Page 14: 11-LINQ to Objects

14

Immediate Execution

If you want to execute a query immediately:• Invoke the ToArray<T>() extension method

Executes the query and places the result in a T[] array• Or invoke the ToList<T>() extension method

Executes the query and places the result in a List<T>

private static void DemoImmediateExecution(){ int[] examMarks = { 62, 75, 85, 59, 96, 80 };

// Execute query immediately, and get result as int[]. int[] arrayOfGradeAs = (from m in examMarks where m >= 70 select m).ToArray<int>();

// Get data RIGHT NOW as List<int>. List<int> listOfGradeAs = (from m in examMarks where m >= 70 select m).ToList<int>();}

private static void DemoImmediateExecution(){ int[] examMarks = { 62, 75, 85, 59, 96, 80 };

// Execute query immediately, and get result as int[]. int[] arrayOfGradeAs = (from m in examMarks where m >= 70 select m).ToArray<int>();

// Get data RIGHT NOW as List<int>. List<int> listOfGradeAs = (from m in examMarks where m >= 70 select m).ToList<int>();}

Page 15: 11-LINQ to Objects

15

3. Using LINQ with Collections

Overview Querying generic collections Querying raw collections Filtering data

Page 16: 11-LINQ to Objects

16

Overview

In this section, we'll show how to use LINQ to query data in a collection• See the LinqOverCollections project

We'll be querying collections of Product objects• Here's the Product classclass Product{ public string Description { get; set; } public double Price { get; set; } public int InStock { get; set; }}

class Product{ public string Description { get; set; } public double Price { get; set; } public int InStock { get; set; }}

Page 17: 11-LINQ to Objects

17

Querying Generic Collections

You can use generic collections in LINQ queries• Generic collections implement IEnumerable<T>

private static void DemoLinqGenericCollection(){ // List<> of Product objects. List<Product> products = new List<Product>() { new Product { Description = "3D HD-TV", Price = 1500, InStock = 3 }, new Product { Description = "BlueRay Player", Price = 200, InStock = 10 }, … };

Console.WriteLine("Expensive products:"); var subset1 = from p in products where p.Price > 300 select p; foreach (var p in subset1) { Console.WriteLine("\t{0}", p.Description); }

Console.WriteLine("Scarce expensive products:"); var subset2 = from p in products where p.Price > 300 && p.InStock < 5 select p; foreach (var p in subset2) { Console.WriteLine("\t{0}", p.Description); }}

private static void DemoLinqGenericCollection(){ // List<> of Product objects. List<Product> products = new List<Product>() { new Product { Description = "3D HD-TV", Price = 1500, InStock = 3 }, new Product { Description = "BlueRay Player", Price = 200, InStock = 10 }, … };

Console.WriteLine("Expensive products:"); var subset1 = from p in products where p.Price > 300 select p; foreach (var p in subset1) { Console.WriteLine("\t{0}", p.Description); }

Console.WriteLine("Scarce expensive products:"); var subset2 = from p in products where p.Price > 300 && p.InStock < 5 select p; foreach (var p in subset2) { Console.WriteLine("\t{0}", p.Description); }}

Page 18: 11-LINQ to Objects

18

Querying Raw Collections

You can't use raw collections in LINQ queries• Raw collections don't implement IEnumerable<T>

• You must convert into an IEnumerable<T> collection

private static void DemoLinqGenericCollection(){ // Raw ArrayList of Product objects. ArrayList products = new ArrayList() { new Product { Description = "3D HD-TV", Price = 1500, InStock = 3 }, new Product { Description = "BlueRay Player", Price = 200, InStock = 10 }, … };

// Transform ArrayList into an IEnumerable<T> collection. var enumerable = products.OfType<Product>();

Console.WriteLine("Expensive products (from raw collection originally):"); var subset = from p in enumerable where p.Price > 300 select p; foreach (var p in subset) { Console.WriteLine("\t{0}", p.Description); }}

private static void DemoLinqGenericCollection(){ // Raw ArrayList of Product objects. ArrayList products = new ArrayList() { new Product { Description = "3D HD-TV", Price = 1500, InStock = 3 }, new Product { Description = "BlueRay Player", Price = 200, InStock = 10 }, … };

// Transform ArrayList into an IEnumerable<T> collection. var enumerable = products.OfType<Product>();

Console.WriteLine("Expensive products (from raw collection originally):"); var subset = from p in enumerable where p.Price > 300 select p; foreach (var p in subset) { Console.WriteLine("\t{0}", p.Description); }}

Page 19: 11-LINQ to Objects

19

Filtering Data

You can filter data in a collection, to retrieve only those objects of a specific type• Use the OfType<T>() extension methodprivate static void DemoFilter(){ // Raw ArrayList of various object types. ArrayList myStuff = new ArrayList() { 3, 12, 1964, true, new Product(), "super swans" };

// Transform ArrayList into an IEnumerable<int>, ignores the non-ints. IEnumerable<int> myInts = myStuff.OfType<int>();

Console.WriteLine("Ints from the raw collection:"); foreach (int i in myInts) { Console.WriteLine("\t{0}", i); }}

private static void DemoFilter(){ // Raw ArrayList of various object types. ArrayList myStuff = new ArrayList() { 3, 12, 1964, true, new Product(), "super swans" };

// Transform ArrayList into an IEnumerable<int>, ignores the non-ints. IEnumerable<int> myInts = myStuff.OfType<int>();

Console.WriteLine("Ints from the raw collection:"); foreach (int i in myInts) { Console.WriteLine("\t{0}", i); }}

Page 20: 11-LINQ to Objects

20

4. LINQ Under the Hood

Overview Using C# query operators LINQ extension methods Using anonymous methods Using lambda expressions

Page 21: 11-LINQ to Objects

21

Overview

In this section, we'll explain what actually happens when you use C# LINQ query operators• See the LinqUnderTheHood project

We'll show how to implement the same LINQ query using:• C# LINQ query operators• Extension methods and anonymous methods• Extension methods and lambda expressions

Page 22: 11-LINQ to Objects

22

Using C# Query Operators

Here's a simple example using C# query operators• Nothing new here private static void DemoOperators(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Create a LINQ query expression using C# query operators. var subset = from c in cities where c.Contains(" ") orderby c.Length select c;

Console.WriteLine("Cities with spaces (using C# query operators): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

private static void DemoOperators(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Create a LINQ query expression using C# query operators. var subset = from c in cities where c.Contains(" ") orderby c.Length select c;

Console.WriteLine("Cities with spaces (using C# query operators): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

Page 23: 11-LINQ to Objects

23

LINQ Extension Methods

The C# query operators are short-hand syntax• Under the cover, they represent extension

methods that are added to IEnumerable<T>• Defined in the System.Linq.Enumerable class• Located in the System.Core.dll assembly

For example:where g.Contains(" ")where g.Contains(" ")

IEnumerable<T> Where<TSrc>(this IEnumerable<TSrc> source, Func<TSrc, bool> predicate)IEnumerable<T> Where<TSrc>(this IEnumerable<TSrc> source, Func<TSrc, bool> predicate)

delegate TResult Func<in TSrc, out TResult>(TSrc item)delegate TResult Func<in TSrc, out TResult>(TSrc item)

References a method that takes an item and returns bool,

indicating whether to keep the item in the query's result set

Page 24: 11-LINQ to Objects

24

Using Anonymous Methods

Here's the equivalent example…• This time using anonymous methods explicitly• Each anonymous method is represented by an

appropriate Func<> delegate

private static void DemoAnonymousMethods(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Build Func<> delegates for Where(), OrderBy(), and Select() extension methods. Func<string, bool> whereFunc = delegate(string c) { return c.Contains(" "); }; Func<string, int> orderbyFunc = delegate(string c) { return c.Length; }; Func<string, string> selectFunc = delegate(string c) { return c; };

// Pass the Func<> delegates into extension methods. var subset = cities.Where(whereFunc) .OrderBy(orderbyFunc) .Select(selectFunc);

Console.WriteLine("\nCities with spaces (using anonymous methods): "); foreach (var c in subset) Console.WriteLine("City: {0}", c);}

private static void DemoAnonymousMethods(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Build Func<> delegates for Where(), OrderBy(), and Select() extension methods. Func<string, bool> whereFunc = delegate(string c) { return c.Contains(" "); }; Func<string, int> orderbyFunc = delegate(string c) { return c.Length; }; Func<string, string> selectFunc = delegate(string c) { return c; };

// Pass the Func<> delegates into extension methods. var subset = cities.Where(whereFunc) .OrderBy(orderbyFunc) .Select(selectFunc);

Console.WriteLine("\nCities with spaces (using anonymous methods): "); foreach (var c in subset) Console.WriteLine("City: {0}", c);}

Page 25: 11-LINQ to Objects

25

Using Lambda Expressions

It's a small step from using anonymous methods to using lambda expressions

Lambda expression syntax reminder:

private static void DemoLambdaExpressions(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Pass lambda expressions into extension methods var subset = cities.Where(c => c.Contains(" ")) .OrderBy(c => c.Length) .Select(c => c);

Console.WriteLine("\nCities with spaces (using lambda expressions): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

private static void DemoLambdaExpressions(){ string[] cities = { "Boston", "New York", "Dallas", "St. Paul", "Las Vegas" };

// Pass lambda expressions into extension methods var subset = cities.Where(c => c.Contains(" ")) .OrderBy(c => c.Length) .Select(c => c);

Console.WriteLine("\nCities with spaces (using lambda expressions): "); foreach (var s in subset) Console.WriteLine("City: {0}", s);}

• Represents a method that takes a single parameter

• Method body returns the boolean result of c.Contains()

Where( c => c.Contains(" ") )Where( c => c.Contains(" ") )

Page 26: 11-LINQ to Objects

26

5. Additional LINQ Techniques

Overview Selecting full objects Selecting specific fields Selecting projections Set-based operations Aggregate operations

Page 27: 11-LINQ to Objects

27

Overview

In this section, we'll show how to use some additional LINQ techniques• See the AdditonalLinqTechniques project

We'll be querying collections of Product objects• Here's the Product class, same as earlier in

chapterclass Product{ public string Description { get; set; } public double Price { get; set; } public int InStock { get; set; }}

class Product{ public string Description { get; set; } public double Price { get; set; } public int InStock { get; set; }}

Page 28: 11-LINQ to Objects

28

Selecting Full Objects

This example recaps how to query full objects• Nothing new here private static void DemoFullObjectQueries(Product[] products){ Console.WriteLine("Low-stock product details:");

var prods = from p in products where p.InStock < 5 select p;

foreach (var p in prods) Console.WriteLine("\t{0}", p);}

private static void DemoFullObjectQueries(Product[] products){ Console.WriteLine("Low-stock product details:");

var prods = from p in products where p.InStock < 5 select p;

foreach (var p in prods) Console.WriteLine("\t{0}", p);}

Page 29: 11-LINQ to Objects

29

Selecting Specific Fields

You can query specific fields from result objects• Specify the required field in the select clauseprivate static void DemoSpecificFieldQueries(Product[] products){ Console.WriteLine("\nLow-stock product prices:");

var prices = from p in products where p.InStock < 5 select p.Price;

foreach (var p in prices) Console.WriteLine("\t{0:c}", p);}

private static void DemoSpecificFieldQueries(Product[] products){ Console.WriteLine("\nLow-stock product prices:");

var prices = from p in products where p.InStock < 5 select p.Price;

foreach (var p in prices) Console.WriteLine("\t{0:c}", p);}

Page 30: 11-LINQ to Objects

30

Selecting Projections

You can query "projections" from result objects• A projection is an anonymous type• Contains the properties specified in the select

clauseprivate static void DemoProjectionQueries(Product[] products){ Console.WriteLine("\nLow-stock products and prices:");

var projs = from p in products where p.InStock < 5 select new { p.Description, p.Price };

foreach (var p in projs) Console.WriteLine("\t{0}, {1:c}", p.Description, p.Price);}

private static void DemoProjectionQueries(Product[] products){ Console.WriteLine("\nLow-stock products and prices:");

var projs = from p in products where p.InStock < 5 select new { p.Description, p.Price };

foreach (var p in projs) Console.WriteLine("\t{0}, {1:c}", p.Description, p.Price);}

Page 31: 11-LINQ to Objects

31

Set-Based Operations (1 of 2)

There are numerous extension methods that allow you to perform set-based operations on queries• Think "Venn-diagrams"

Set-based extension methods:• Except()• Intersect()• Union()• Concat()

Additional useful extension methods:• Count()• Distinct()• Reverse()

Page 32: 11-LINQ to Objects

32

Set-Based Operations (2 of 2)

These examples illustrate set-based operations• … based on the following collectionsList<string> myTeams = new List<String> { "Swansea", "Arsenal", "Liverpool", "Blackpool" };

List<string> yourTeams = new List<String> { "Swansea", "Man Utd", "West Brom", "Liverpool" };

List<string> myTeams = new List<String> { "Swansea", "Arsenal", "Liverpool", "Blackpool" };List<string> yourTeams = new List<String> { "Swansea", "Man Utd", "West Brom", "Liverpool" };

// Teams I like but you don't…var diff = (from t in myTeams select t).Except(from t2 in yourTeams select t2);

// Teams I like but you don't…var diff = (from t in myTeams select t).Except(from t2 in yourTeams select t2);

// Teams we both like…var intersect = (from t in myTeams select t).Intersect(from t2 in yourTeams select t2);

// Teams we both like…var intersect = (from t in myTeams select t).Intersect(from t2 in yourTeams select t2);

// Teams either of us likes…var union = (from t in myTeams select t).Union(from t2 in yourTeams select t2);

// Teams either of us likes…var union = (from t in myTeams select t).Union(from t2 in yourTeams select t2);

// Concatenation of my teams and your teams…var concat = (from t in myTeams select t).Concat(from t2 in yourTeams select t2);

// Concatenation of my teams and your teams…var concat = (from t in myTeams select t).Concat(from t2 in yourTeams select t2);

// Distinct concatenation of my teams and your teams…var distinct = (from t in myTeams select t).Concat(from t2 in yourTeams select t2) .Distinct();

// Distinct concatenation of my teams and your teams…var distinct = (from t in myTeams select t).Concat(from t2 in yourTeams select t2) .Distinct();

Page 33: 11-LINQ to Objects

33

Aggregate Operations

LINQ provides various "aggregate" extension methods• Familiar concept for SQL developers

private static void DemoAggregates(){ double[] winterTemps = { 2.0, -3.3, 8, -4, 0, 8.2 };

Console.WriteLine("\nAggregate query results:"); Console.WriteLine("Max: {0:f2}", (from t in winterTemps select t).Max()); Console.WriteLine("Min: {0:f2}", (from t in winterTemps select t).Min()); Console.WriteLine("Avg: {0:f2}", (from t in winterTemps select t).Average()); Console.WriteLine("Sum: {0:f2}", (from t in winterTemps select t).Sum());}

private static void DemoAggregates(){ double[] winterTemps = { 2.0, -3.3, 8, -4, 0, 8.2 };

Console.WriteLine("\nAggregate query results:"); Console.WriteLine("Max: {0:f2}", (from t in winterTemps select t).Max()); Console.WriteLine("Min: {0:f2}", (from t in winterTemps select t).Min()); Console.WriteLine("Avg: {0:f2}", (from t in winterTemps select t).Average()); Console.WriteLine("Sum: {0:f2}", (from t in winterTemps select t).Sum());}

Page 34: 11-LINQ to Objects

34

Summary

Introduction to LINQ Using LINQ with arrays Using LINQ with collections LINQ under the hood Additional LINQ techniques