开发工具与关键技术:LINQ查询
作者:邓崇富
撰写时间:2019 年6 月 24 日
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LINQ概述:
LINQ(Language Integrated Query,语言集成查询)在C#编程语言中集成了查询语法,可以用相同的语法访问不同的数据源,LINQ提供了不同数据源的抽象层,所以可以使用相同的语法。
列表和实体:
为了下面的LINQ查询演示,需要准备一些实体和列表来存放一些数据,查询的数据在一个包含1950至2015一级方程式锦标赛的集合上。
对于实体,定义类型Racer。Racer定义了几个属性和一个重载的ToString()方法,该方法以字符串格式显示赛车手。这个类实现了IFormattable接口,,以支持格式字符串的不同变体,这个类还实现了IComparable<Racer>接口,它根据Lastname为一组赛车手排序。为了执行更高级的查询,Racer类不仅包含单值属性,如Lastname、Wins、Country和Starts,还包含多值属性,如Cars和Years。Years属性列出了赛车手获得冠军的年份。一些赛车手曾多次获得冠军。Cars属性用于列出赛车手在获得冠军的年份中使用的所有车型。
详细代码如下:
namespace Wrox.ProCSharp.LINQ
{
public class Racer:IComparable<Racer>,IFormattable
{
public Racer(string firstName,string lastName,string country, int starts,int wins)
: this(firstName, lastName, country, starts, wins, null, null)
{
}
public Racer (string firstName,string lastName, string country, int starts,int wins, IEnumerable<int> years, IEnumerable<string> cars)
{
FirstName = firstName;
LastName = lastName;
Country = country;
Starts = starts;
Wins = wins;
Years = years != null ? new List<int>(years) : new List<int>();
Cars = cars != null ? new List<string>(cars) : new List<string>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
public int Starts { get; set; }
public int Wins { get; set; }
public IEnumerable<int> Years { get; set; }
public IEnumerable<string> Cars { get; set; }
public override string ToString() => $"{FirstName}{LastName}";
public int CompareTo(Racer other) => LastName.CompareTo(other?.LastName);
public string ToString(string format) => 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 $"{FirstName}{LastName},{Country};starts:{Starts},wins:{Wins}";
default:
throw new FormatException($"Format {format} not supported");
}
}
}
}
第二个实体类是Team。这个类包含车队冠军的名字和获得冠军的年份。与赛车手冠军类似,针对一年中最好的车队也有一个冠军奖项。
详细代码如下:
namespace Wrox.ProCSharp.LINQ
{
public class Team
{
public Team(string name,params int[] years)
{
Name = name;
Years = years != null ? new List<int>(years) : new List<int>();
}
public string Name { get;}
public IEnumerable<int> Years { get; }
}
}
Formulal类在GetChampions()方法中返回一组赛车手。这个列表包含了1950至2015年之间的所有一级方程式冠军。详细代码如下:
public static class Formulal
{
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", 33, 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" })
//..........
}
return _racers;
}
}
对于后面在多个列表的执行的查询,GetContructorChampions()方法返回所有的车队冠军的列表,车队冠军是从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),
new Team("BRM",1962),
new Team("Lotus",1963,1965,1968,1970,1972,1973,1978),
new Team("Brabham",1966,1967)
//................
};
};
return _teams;
}
列表和实体准备好后,下面进行LINQ查询,例如,查询出来自巴西的所有世界冠军,并按照夺冠次数排序。为此可以使用List<T>类的方法,如FindAll()和Sort()方法。而使用LINQ的语法非常简单。代码如下:
private static void Lin1Query()
{
var query = from r in Formulal.GetChampions()
where r.Country == "Brazil"
orderby r.Wins descending
select r;
foreach (Racer r in query)
{
writeLine($"{r:A}");
}
}
是一个LINQ查询。字句from、where、orderby、descending和select 都是这个查询中预定义的关键。查询表达式必须以from字句开头,以select或group字句结束。在这两个字句之间,可以使用where、orderby、join、let和其他from字句。