三、使用Linq进行查询

有了这些预备知识和实体类,我们就可以使用Linq进行查询了。

       1、简单的筛选

使用Where子句可以对数据源进行简单的筛选。下变例子是找出至少赢得15场比赛的奥地利车手:

  1. /// <summary>  
  2. /// 1、最简单的查询  
  3. /// </summary>  
  4. static void LinqQuery1()  
  5. {  
  6.     var query = from r in Formula1.GetChampions()  
  7.                 where (r.Country == "Brazil" || r.Country == "Austria") && r.Wins > 15  
  8.                 orderby r.Wins descending  
  9.                 select r;  
  10.     foreach (var racer in query)  
  11.     {  
  12.         Console.WriteLine("{0:A}", racer);  
  13.     }  
  14. }  

这段代码的功能可以使用System.Linq中的扩展方法来实现:

【代码】

  1. var query2 = Formula1.GetChampions()  
  2.       .Where(r => r.Country == "Brazil" || r.Country == "Austria")  
  3.       .Select(r=>r);  

注:并不是所有的查询都可以使用Linq查询或者扩展方法都可以实现的。高级查询需要使用扩展方法。

2、用索引进行筛选

Where()扩展方法的一个重载中,可以对该方法传递第二个参数:索引。索引是筛选器返回的每个结果的计数器,可以在表达式中使用索引,执行一些索引相关的计算。现编的代码使用Where()扩展方法,返回姓氏以A开头,索引为偶数的车手:

  1. /// <summary>  
  2. /// 2、使用索引i进行选   
  3. /// </summary>  
  4.         static void LinqQuery2()  
  5.         {  
  6.   
  7.             //使用索引i进行筛选  
  8.              var query = Formula1.GetChampions()  
  9.                  .Where((r, i) => r.LastName.StartsWith("A") && i % 2 != 0);  
  10.             foreach (var item in query)  
  11.             {  
  12.                 Console.WriteLine("{0:A}", item);  
  13.             }  
  14.         }  

 

       3、筛选出不同的数据类型

使用OfType()扩展方法,可以实现基于类型的筛选。下面的代码中定义了包含了string和int类型的对象,使用OfType()方法从集合中找出字符串:

  1. /// <summary>  
  2. /// 3、类型筛选           
  3. /// </summary>  
  4. static void LinqQuery3()  
  5. {  
  6.     object[] data = { "one", 1, 2, 3, "two""three" };  
  7.     var query = data.OfType<string>();  
  8.   
  9.     foreach (var item in query)  
  10.     {  
  11.         Console.WriteLine(item);  
  12.     }  
  13. }  

       4、复合的from子句

复合的from子句用于对这样一种情况的查询:需要根据对象的一个成员进行筛选,而这个成员本身是一组数据。

本例中,Racer类定义的属性Cars就是这样的一个属性。Cars是一个字符串数组。

使用如下的Linq查询可以筛选出嘉实法拉利的所有冠军:

  1. var query = from r in Formula1.GetChampions()  
  2.             from c in r.Cars  
  3.             where c == "Ferrari"  
  4.             orderby r.LastName  
  5.             select r;  

 其中,第一个from子句用于访问从Formyla1.GetChampions方法返回的Racer对象,第二个from子句访问Racer类的Cars属性,返回所有string类型的赛车,最后使用Where子句从这些赛车中筛选出所有冠军。

这里C#编译器将符合的from子句和Linq查询转换成SelectMany()方法,SelectMany()方法可以用于迭代序列的序列。使用SelectMany()扩展方法的代码如下:

  1. var query = Formula1.GetChampions()  
  2.     .SelectMany(r => r.Cars, (r, c) => new { Racer = r, Car = c })  
  3.     .Where(r => r.Car == "Ferrari")  
  4.     .OrderBy(r => r.Racer.FirstName)  
  5.     .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()扩展方法可以实现对筛选结果的排序。

下面这段代码是对筛选出来的赛手使用赢得比赛的次数进行排序:

  1. //简单的Linq语句查询排序  
  2. var query1 = from r in Formula1.GetChampions()  
  3.             where r.Country == "Brazil"  
  4.             orderby r.Wins descending  
  5.             select r;  

转换成扩展方法后的实现:

  1. //使用扩展方法  
  2. var query2 = Formula1.GetChampions()  
  3.     .Where(r => r.Country == "Brazil")  
  4.     .OrderByDescending(r => r.Wins)  
  5.     .ThenByDescending(r=>r.LastName)  
  6.     .Select(r => r);  

最后还可以使用Take方法对结果进一步筛选:

    1. //使用Take子句提取前十项数据  
    2. var query3 = (from r in Formula1.GetChampions()  
    3.              orderby r.Country, r.LastName, r.FirstName  
    4.              select r)  
    5.                  .Take(10);  
    6.   
    7. //使用扩展方法查询  
    8. var query = Formula1.GetChampions()  
    9.     .OrderBy(r => r.Country)  
    10.     .ThenBy(r => r.LastName)  
    11.     .ThenBy(r => r.FirstName)  
    12.     .Take(10)  
    13.     .Select(r => r);