Sei sulla pagina 1di 7

LINQ

LINQ (Language Integrated Query) integrates query syntax inside the C# programming language and makes it possible to access different data sources with the same syntax. LINQ makes this possible by offering an abstraction layer. The C# language offers integrated query language that is converted to method calls.

Lists and Entities


The LINQ queries in this chapter will be done on a collection containing Formula1 champions from 1950 to 2008. This data needs to be prepared with entity classes and lists. For the entities, the type Racer is defi ned. Racer defi nes several properties and an overloaded ToString() method to display a racer in a string format. This class implements the interface IFormattable to support different variants of format strings, and the interface IComparable<Racer>, which can be used to sort a list of racers based on the LastName. For doing more advanced queries, the class Racer contains not only single value properties such as FirstName, LastName, Wins, Country, and Starts, but also multivalue properties such as Cars and Years. The Years property lists all the years of the championship title. Some racers have won more than one title. The Cars property is used to list all the cars that have been used by the driver during the title years.

using System; using System.Text; namespace Wrox.ProCSharp.LINQ { [Serializable] public class Racer: IComparable<Racer>, IFormattable { public Racer(string firstName = null, string lastName = null, string country = null, int starts = 0, int wins = 0, IEnumerable<int> years = null, IEnumerable<string> cars = null) { this.FirstName = firstName; this.LastName = lastName; this.Country = country; this.Starts = starts; this.Wins = wins; var yearsList = new List<int>(); foreach (var year in years) { yearsList.Add(year); } this.Years = yearsList.ToArray(); var carList = new List<string>(); foreach (var car in cars) { carList.Add(car); } this.Cars = carList.ToArray(); } public string FirstName {get; set;} public string LastName {get; set;} public int Wins {get; set;}

public string Country {get; set;} public int Starts {get; set;} public string[] Cars { get; private set; } public int[] Years { get; private set; } public override string ToString() { return String.Format("{0} {1}", FirstName, LastName); } public int CompareTo(Racer other) { if (other == null) throw new ArgumentNullException("other"); return this.LastName.CompareTo(other.LastName); } public string ToString(string format) { return ToString(format, null); } public string ToString(string format, IFormatProvider formatProvider) { switch (format) { case null: case "N": return ToString(); case "F": return FirstName; case "L": return LastName; case "C": return Country; case "S": return Starts.ToString(); case "W": return Wins.ToString(); case "A": return String.Format("{0} {1}, {2}; starts: {3}, wins: {4}", FirstName, LastName, Country, Starts, Wins); default: throw new FormatException(String.Format( "Format {0} not supported", format)); } } } }

code snippet DataLib/Racer.cs A second entity class is Team. This class just contains the name and an array of years for constructor championships.
[Serializable] public class Team { public Team(string name, params int[] years) { this.Name = name; this.Years = years; }

public string Name { get; private set; } public int[] Years { get; private set; } }

code snippet DataLib/Team.cs The class Formula1 returns a list of racers in the method GetChampions(). The list is filled with all Formula-1 champions from the years 1950 to 2008:
using System.Collections.Generic; namespace Wrox.ProCSharp.LINQ { public static class Formula1 { private static List<Racer> racers; public static IList<Racer> GetChampions() { if (racers == null) { racers = new List<Racer>(40); racers.Add(new Racer("Nino", "Farina", "Italy", 33, 5, new int[] { 1950 }, new string[] { "Alfa Romeo" })); racers.Add(new Racer("Alberto", "Ascari", "Italy", 32, 10, new int[] { 1952, 1953 }, new string[] { "Ferrari" })); racers.Add(new Racer("Juan Manuel", "Fangio", "Argentina", 51, 24, new int[] { 1951, 1954, 1955, 1956, 1957 }, new string[] { "Alfa Romeo", "Maserati", "Mercedes", "Ferrari" })); racers.Add(new Racer("Mike", "Hawthorn", "UK", 45, 3, new int[] { 1958 }, new string[] { "Ferrari" })); racers.Add(new Racer("Phil", "Hill", "USA", 48, 3, new int[] { 1961 }, new string[] { "Ferrari" })); racers.Add(new Racer("John", "Surtees", "UK", 111, 6, new int[] { 1964 }, new string[] { "Ferrari" })); racers.Add(new Racer("Jim", "Clark", "UK", 72, 25, new int[] { 1963, 1965 }, new string[] { "Lotus" })); racers.Add(new Racer("Jack", "Brabham", "Australia", 125, 14, new int[] { 1959, 1960, 1966 }, new string[] { "Cooper", "Brabham" })); racers.Add(new Racer("Denny", "Hulme", "New Zealand", 112, 8, new int[] { 1967 }, new string[] { "Brabham" })); racers.Add(new Racer("Graham", "Hill", "UK", 176, 14, new int[] { 1962, 1968 }, new string[] { "BRM", "Lotus" })); racers.Add(new Racer("Jochen", "Rindt", "Austria", 60, 6, new int[] { 1970 }, new string[] { "Lotus" })); racers.Add(new Racer("Jackie", "Stewart", "UK", 99, 27, new int[] { 1969, 1971, 1973 }, new string[] { "Matra", "Tyrrell" })); //... return racers;

} } } }

code snippet DataLib/Formula1.cs Where queries are done across multiple lists, the GetConstructorChampions() method that follows returns the list of all constructor championships. Constructor championships have been around since 1958.
private static List<Team> teams; public static IList<Team> GetContructorChampions() { if (teams == null) { teams = new List<Team>() { new Team("Vanwall", 1958), new Team("Cooper", 1959, 1960), new Team("Ferrari", 1961, 1964, 1975, 1976, 1977, 1979, 1982, 1983, 1999, 2000, 2001, 2002, 2003, 2004, 2007, 2008), new Team("BRM", 1962), new Team("Lotus", 1963, 1965, 1968, 1970, 1972, 1973, 1978), new Team("Brabham", 1966, 1967), new Team("Matra", 1969), new Team("Tyrrell", 1971), new Team("McLaren", 1974, 1984, 1985, 1988, 1989, 1990, 1991, 1998), new Team("Williams", 1980, 1981, 1986, 1987, 1992, 1993, 1994, 1996, 1997), new Team("Benetton", 1995), new Team("Renault", 2005, 2006 ) }; } return teams; }

LINQ query
Using these prepared lists and entities, you can do a LINQ query, for example, a query to get all world champions from Brazil sorted by the highest number of wins. To accomplish this you could use methods of the List<T> class; e.g. the FindAll() and Sort() methods. However, using LINQ theres a simpler syntax as soon as youre used to it:
private static void LinqQuery() { var query = from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r; foreach (Racer r in query) { Console.WriteLine("{0:A}", r); } }

code snippet LINQIntro/Program.cs

The result of this query shows world champions from Brazil ordered: Ayrton Senna, Brazil; starts: 161, wins: 41 Nelson Piquet, Brazil; starts: 204, wins: 23 Emerson Fittipaldi, Brazil; starts: 143, wins: 14 The statement
from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r;

is a LINQ query. The clauses from, where, orderby, descending, and select are predefined keywords in this query. The query expression must begin with a from clause and end with a select or group clause. In between you can optionally use where, orderby, join, let, and additional from clauses. It is important to note that the variable query just has the LINQ query assigned to it. The query is not performed by this assignment but rather as soon as the query is accessed using the foreach loop. Extension Methods The extension methods here are defined in the namespace System.Linq in the assembly System.Core Now it s possible to use the extension methods Where() , OrderByDescending() , and Select() from the class Enumerable . Because each of these methods returns IEnumerable < TSource >, it is possible to invoke one method after the other by using the previous result. With the arguments of the extension methods, anonymous methods that defi ne the implementation for the delegate parameters are used:
static void ExtensionMethods() { var champions = new List < Racer > (Formula1.GetChampions()); IEnumerable < Racer > brazilChampions = champions.Where(r = > r.Country == "Brazil"). OrderByDescending(r = > r.Wins). Select(r = > r); foreach (Racer r in brazilChampions) { Console.WriteLine("{0:A}", r); } }

Deferred Query Execution


When the query expression is defined during runtime, the query does not run. The query runs when the items are iterated.

This has a very interesting and important effect. With the following example a collection of String elements is created and filled with the name arr. Next, a query is defined to get all names from the collection where the item starts with the letter J. The collection should also be sorted. The iteration does not happen when the query is defined. Instead, the iteration happens with the foreach statement, where all items are iterated. Only one element of the collection fulfils the requirements of the where expression by starting with the letter J: Juan. After the iteration is done and Juan is written to the console, four new names are added to the collection. Then the iteration is done again:
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" }; var namesWithJ = from n in names where n.StartsWith("J") orderby n select n; Console.WriteLine("First iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); } Console.WriteLine(); names.Add("John"); names.Add("Jim"); names.Add("Jack"); names.Add("Denny"); Console.WriteLine("Second iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); }

Because the iteration does not happen when the query is defined, but it does happen with every foreach, changes can be seen, as the output from the application demonstrates:
First iteration Juan Second iteration Jack Jim John Juan

Of course, you also must be aware that the extension methods are invoked every time the query is used within an iteration. Most of the time this is very practical, because you can detect changes in the source data. However, there are situations where this is impractical. You can change this behavior by invoking the extension methods ToArray(), ToEnumerable(), ToList(), and the like. In the example, you can see that ToList iterates through the collection immediately and returns a collection implementing IList<string>. The returned list is then iterated through twice; in between iterations, the data source gets new names:
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" }; var namesWithJ = (from n in names where n.StartsWith("J")

orderby n select n).ToList(); Console.WriteLine("First iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); } Console.WriteLine(); names.Add("John"); names.Add("Jim"); names.Add("Jack"); names.Add("Denny"); Console.WriteLine("Second iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); }

In the result, you can see that in between the iterations the output stays the same although the collection values have changed:
First iteration Juan Second iteration Juan

Potrebbero piacerti anche