11-linq to objects
TRANSCRIPT
LINQ to Objects
Chapter 11
C# Development
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
3
1. Introduction to LINQ
What is LINQ? The purpose of LINQ LINQ variations A few technical details
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
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.
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
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
8
2. Using LINQ with Arrays
Overview Querying arrays without LINQ Querying arrays with LINQ C# LINQ query operators Deferred execution Immediate execution
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
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); }}
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);}
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
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);}
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>();}
15
3. Using LINQ with Collections
Overview Querying generic collections Querying raw collections Filtering data
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; }}
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); }}
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); }}
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); }}
20
4. LINQ Under the Hood
Overview Using C# query operators LINQ extension methods Using anonymous methods Using lambda expressions
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
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);}
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
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);}
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(" ") )
26
5. Additional LINQ Techniques
Overview Selecting full objects Selecting specific fields Selecting projections Set-based operations Aggregate operations
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; }}
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);}
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);}
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);}
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()
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();
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());}
34
Summary
Introduction to LINQ Using LINQ with arrays Using LINQ with collections LINQ under the hood Additional LINQ techniques