使用group子句和GroupBy()扩展方法可以对查询结果进行分组。下边的例子是将赛车冠军按照国家进行分组,并列出一个国家赛车冠军的总数:
//简ò单蹋inq分?组哩?
var query = from r in Formula1.GetChampions()
group r by r.Country into g
orderby g.Count() descending, g.Key
where g.Count() >= 2
select new
{
Country = g.Key,
Count = g.Count()
};
子句group r by r.Country into g 根据Country属性组合所有赛车手到一个新的标识符g中。g用于以后访问分组的结果信息。group子句的结果根据扩展方法Count()的结果进行排序。Select子句创建了一个带Country和Count属性的匿名类型。这里有一个对象需要注意:g.Key。这指的是是group方法筛选的依据,本例中g.Key就是分组依据Country。
直接使用扩展方法中的GroupBy()和ThenBy()方法也可以实现相应的筛选功能:
var query = Formula1.GetChampions()
.GroupBy(r => r.Country)
.OrderByDescending(g => g.Count())
.ThenBy(g => g.Key)
.Where(g => g.Count() >= 2)
.Select(g => new { Country = g.Key, Count = g.Count() });
这里可以看出子句group r by r.Country into g被解析为GroupBy(r=>r.Country),返回分组序列,之后进行了排序和筛选等见大的操作即得到了需要的结果。
7、对嵌套对象进行分组
如果分组的对象应包含嵌套的序列,就可以改变select子句创建的匿名类型。先看下面的代码:
var countrys =
from r in Formula1.GetChampions()
group r by r.Country into g //g是?按恪?照?国ú别纄分?组哩?后ó的?Formula1.GetChampions()
orderby g.Count() descending, g.Key //g.Key指?的?是?分?组哩?依皑?据Yr.Country 即′r中D的?Country
where g.Count() >= 2
select
new
{
Country = g.Key,
Count = g.Count(),
Racer = from r1 in g
orderby r1.LastName
select r1.FirstName + " " + r1.LastName
};
在上面的例子中,返回的国家不仅包含国家名和赛手数量这两个属性,还包括赛手姓名序列。这个序列用一个赋予Racers属性的from/in内部子句指定,内部的from子句使用分组标识符g获取该分组中的所有赛车手,并排序,再根据姓名创建一个新的字符串返回。
这段代码转换成扩展方法可表示如下:
var countrys = Formula1.GetChampions()
.GroupBy(r => r.Country)
.OrderByDescending(g => g.Count())
.ThenBy(g => g.Key)
.Where(g => g.Count() >= 2)
.Select(g => new
{
Country = g.Key,
Count = g.Count(),
Racer = g.OrderBy(r => r.LastName).Select(r => r.FirstName + " " + r.LastName)
});
8、连接查询
使用join子句可以根据特定的条件合并两个数据源,但之前要获得两个要连接的列表。
在一级方程式比赛中,有比赛冠军和车队冠军,下边的代码用来筛选出每年的赛手冠军和车队冠军。实现这一功能有几种方法,下面一一列举:
(1)使用多次查询
先定义两个查询,分别找出2003年之后每年的赛手冠军和车队冠军:
//1、查询1 var racer = from r in Formula1.GetChampions()
from y in r.Years
where y > 2003
select new
{
Year = y,
Name = r.FirstName + " " + r.LastName
};
//2、查询2 var teams = from t in Formula1.GetConstructorChampions()
from y in t.Years
where y > 2003
select new
{
Year = y,
Name = t.Name
};
之后在通过join in …on…进行连接:
//3、将两次查询结果连接
var query = from r in racer
join t in teams on r.Year equals t.Year
select new
{
Year = r.Year,
Racer = r.Name,
Team = t.Name
};
(2)也可将两次查询合并为一个Linq查询 ,不过比较麻烦的说:
//使用一条语句完成所有连接查询
var query2 = from r in
from r1 in Formula1.GetChampions()
from yr in r1.Years
where yr > 2003
select new
{
Year = yr,
Name = r1.FirstName + " " + r1.LastName
}
join t in
from t1 in Formula1.GetConstructorChampions()
from yt in t1.Years
where yt > 2003
select new
{
Year = yt,
Name = t1.Name
}
on r.Year equals t.Year
select new
{
Year = r.Year,
Racer = r.Name,
Team = t.Name
};
9、集合查询
System.Linq中的扩展方法Distinct()、Union()、Intersect()、Except()的都是集合操作的方法。这些方法使用不难,主要涉及到一些对集合操作的理解上。
下面用一段代码演示集合操作符的使用:
//1、先进性一次简单的查询
var query = from r in Formula1.GetChampions()
from y in r.Years
where y > 2003
select r;
//2、再对结果进行集合操作.这里的几个操作没有实际意义,只是为了演示用法。
var q = query.Union(query).Distinct().Intersect(query);
上面的代码中,先进行了一个间的的查询,之后对查询结果进行了集合操作。以下是上面代码转换成使用扩展方法的代码:
//3、直接使用扩展方法进行查询
var query2 = Formula1.GetChampions()
.SelectMany(r => r.Years, (r, y) => new { Racer = r, Year = y }) //使用了SelectMany()嵌套查询
.Where(r => r.Year > 2003)
.Select(r => r.Racer)
.Union(query).Distinct().Intersect(query); //仅演示,没实际意义
10、合并
合并操作Zip()是.NET4新增的。这个操作运行用一个谓词函数把两个相关的序列合并为一个。我不是很理解这个合并操作,仅给出树上的代码,不做解释:
var racerNames = from r in Formula1.GetChampions()
where r.Country == "Italy"
orderby r.Wins descending
select new
{
Name = r.FirstName + " " + r.LastName
};
var racerNameAndStarts = from r in Formula1.GetChampions()
where r.Country == "Italy"
orderby r.Wins descending
select new
{
LastName = r.LastName,
Starts = r.Starts
};
//.Net4中的Zip()方法。用一个谓词函数把两个相关的序列合并为一个。注意Zip的参数
var racers = racerNames.Zip(racerNameAndStarts, (first, second) => first.Name + ",starts " + second.Starts);
foreach (var item in racers)
{
Console.WriteLine(item);
}