三、使用Linq进行查询
有了这些预备知识和实体类,我们就可以使用Linq进行查询了。
1、简单的筛选
使用Where子句可以对数据源进行简单的筛选。下变例子是找出至少赢得15场比赛的奥地利车手:
- /// <summary>
- /// 1、最简单的查询
- /// </summary>
- static void LinqQuery1()
- {
- var query = from r in Formula1.GetChampions()
- where (r.Country == "Brazil" || r.Country == "Austria") && r.Wins > 15
- orderby r.Wins descending
- select r;
- foreach (var racer in query)
- {
- Console.WriteLine("{0:A}", racer);
- }
- }
这段代码的功能可以使用System.Linq中的扩展方法来实现:
【代码】
- var query2 = Formula1.GetChampions()
- .Where(r => r.Country == "Brazil" || r.Country == "Austria")
- .Select(r=>r);
注:并不是所有的查询都可以使用Linq查询或者扩展方法都可以实现的。高级查询需要使用扩展方法。
2、用索引进行筛选
Where()扩展方法的一个重载中,可以对该方法传递第二个参数:索引。索引是筛选器返回的每个结果的计数器,可以在表达式中使用索引,执行一些索引相关的计算。现编的代码使用Where()扩展方法,返回姓氏以A开头,索引为偶数的车手:
- /// <summary>
- /// 2、使用索引i进行选
- /// </summary>
- static void LinqQuery2()
- {
- //使用索引i进行筛选
- var query = Formula1.GetChampions()
- .Where((r, i) => r.LastName.StartsWith("A") && i % 2 != 0);
- foreach (var item in query)
- {
- Console.WriteLine("{0:A}", item);
- }
- }
3、筛选出不同的数据类型
使用OfType()扩展方法,可以实现基于类型的筛选。下面的代码中定义了包含了string和int类型的对象,使用OfType()方法从集合中找出字符串:
- /// <summary>
- /// 3、类型筛选
- /// </summary>
- static void LinqQuery3()
- {
- object[] data = { "one", 1, 2, 3, "two", "three" };
- var query = data.OfType<string>();
- foreach (var item in query)
- {
- Console.WriteLine(item);
- }
- }
4、复合的from子句
复合的from子句用于对这样一种情况的查询:需要根据对象的一个成员进行筛选,而这个成员本身是一组数据。
本例中,Racer类定义的属性Cars就是这样的一个属性。Cars是一个字符串数组。
使用如下的Linq查询可以筛选出嘉实法拉利的所有冠军:
- var query = from r in Formula1.GetChampions()
- from c in r.Cars
- where c == "Ferrari"
- orderby r.LastName
- select r;
其中,第一个from子句用于访问从Formyla1.GetChampions方法返回的Racer对象,第二个from子句访问Racer类的Cars属性,返回所有string类型的赛车,最后使用Where子句从这些赛车中筛选出所有冠军。
这里C#编译器将符合的from子句和Linq查询转换成SelectMany()方法,SelectMany()方法可以用于迭代序列的序列。使用SelectMany()扩展方法的代码如下:
- var query = Formula1.GetChampions()
- .SelectMany(r => r.Cars, (r, c) => new { Racer = r, Car = c })
- .Where(r => r.Car == "Ferrari")
- .OrderBy(r => r.Racer.FirstName)
- .Select(r => r.Racer.FirstName + " " + r.Racer.LastName);
这里SelectMany()方法的第一个参数是隐式参数,他从GetChampions方法中接收Racer对象序列,第二个参数是 collectionSelector委托,其中定义了内部序列。在Lambda表达式r=>r.Cars中,返回赛车集合。第三个参数是一个 Func<T>委托,这里为每个Car调用该委托接收Racer和Cars对象。Lambda表达式创建了一个包含了Racer和Cars属 性的匿名的类型。这个SelectMany方法的结果摊平了赛手和赛车的层次结构,为每个赛车返回匿名类型的一个新对象耦合。
这段解释有点拗口,因为Lambda表达式确实比较难以解释,还有使用了几个Func<T>委托,不了解委托看这段代码简直就是天书。不过VS的职能提示挺管用的,在敲出几个代码之后看看他的提示在继续写也是不错的。
5、对筛选结果进行排序
使用orderby和orderby descending子句或者OrderBy()和OrderByDescending()扩展方法可以实现对筛选结果的排序。
下面这段代码是对筛选出来的赛手使用赢得比赛的次数进行排序:
- //简单的Linq语句查询排序
- var query1 = from r in Formula1.GetChampions()
- where r.Country == "Brazil"
- orderby r.Wins descending
- select r;
转换成扩展方法后的实现:
- //使用扩展方法
- var query2 = Formula1.GetChampions()
- .Where(r => r.Country == "Brazil")
- .OrderByDescending(r => r.Wins)
- .ThenByDescending(r=>r.LastName)
- .Select(r => r);
最后还可以使用Take方法对结果进一步筛选:
- //使用Take子句提取前十项数据
- var query3 = (from r in Formula1.GetChampions()
- orderby r.Country, r.LastName, r.FirstName
- select r)
- .Take(10);
- //使用扩展方法查询
- var query = Formula1.GetChampions()
- .OrderBy(r => r.Country)
- .ThenBy(r => r.LastName)
- .ThenBy(r => r.FirstName)
- .Take(10)
- .Select(r => r);