linq group

本篇介绍Linq的Group和Join操作,继续使用《Linq 学习(3) 语法结构》中介绍的数据源。

Group
Group是进行分组操作,同SQL中的Group By类似。
原型如下:

public  static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>( 
     this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector)


它有几个重载,返回类型有两种:IEnumerable<IGrouping<TKey, TSource>> 和 IEnumerable<TResult>。
返回类型为:IEnumerable<IGrouping<TKey, TSource>>
示例:
返回按学号分组学生的成绩

var result =  from score  in DataSource.Scores 
              group score  by score.StudentID  into scoreGroup 
             select scoreGroup;


scoreGroup为IGrouping<TKey, TSource>类型,返回结果为IEnumerable<IGrouping<TKey, TSource>>,既集合的集合,因此输出时需用双重循环。
IGrouping<TKey, TElement>接口定义为:

public  interface IGrouping<TKey, TElement> : IEnumerable<TElement>, IEnumerable 

    TKey Key {  get; } 
}

其中Key为分组依据的字段。

foreach ( var  group  in result) 

     // 输出分组依据的字段  
    Console.WriteLine( " \nStudent ID: " +  group.Key); 

     //  输出组内成员  
     foreach ( var score  in  group
    { 
        Console.WriteLine(score); 
    } 


//  result: 
//  Student ID:1 
//  Student ID:1,Course ID:1,Score:78 
//  Student ID:1,Course ID:2,Score:60 
//  ... 

//  Student ID:2 
//  Student ID:2,Course ID:1,Score:59 
//  ...


等效的扩展方法调用实现为:

var result = DataSource.Scores.GroupBy(score => score.StudentID);


返回类型为:IEnumerable<TResult>
对分组结果进行一些包装,如包装为匿名类型。
返回按学号分组学生的成绩

var result =  from score  in DataSource.Scores 
              group score  by score.StudentID  into scoreGroup 
             select  new { StudentID = scoreGroup.Key, Group = scoreGroup };


匿名类型中Group为IGrouping<TKey, TSource>类型。
等效的扩展方法调用实现为:

var result = DataSource.Scores.GroupBy(score => score.StudentID, 
    (key,  group) =>  new { StudentID = key, Group =  group });


其他一些重载使用方法类似。

Join

连接操作。

public  static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>( 
     this IEnumerable<TOuter> outer, 
    IEnumerable<TInner> inner, 
    Func<TOuter, TKey> outerKeySelector, 
    Func<TInner, TKey> innerKeySelector, 
    Func<TOuter, TInner, TResult> resultSelector)


从Join方法原型可以看出其使用方法。

内连接
选择左右两侧集合都含有相对应的元素。
示例:
查询学生的姓名、学科、成绩。

var  result  =   from  score  in  DataSource.Scores 
             
join  student  in  DataSource.Students  on  score.StudentID  equals  student.StudentID 
             
join  course  in  DataSource.Courses  on  score.CourseID  equals  course.CourseID 
             select 
new  { StudentName  =  student.Name, CourseName  =  course.CourseName, ScoreValue  =  score.Value }; 
//  result 
//  { StudentName = Andy, CourseName = C Language, ScoreValue = 78 } 
//  { StudentName = Andy, CourseName = Biophysics, ScoreValue = 60 } 
//  ... 
//  { StudentName = Bill, CourseName = C Language, ScoreValue = 59 } 
//  { StudentName = Cindy, CourseName = Biophysics, ScoreValue = 60 } 
//  ...


等效的扩展方法调用实现为:

var  result  =  
    DataSource.Scores.Join( 
    DataSource.Students, 
    score 
=>  score.StudentID, 
    student 
=>  student.StudentID, 
    (score, student) 
=>   new  { StudentName  =  student.StudentID, ScoreValue  =  score.Value, CourseID  =  score.CourseID }) 
    .Join(DataSource.Courses, 
    scostu 
=>  scostu.CourseID, 
    course 
=>  course.CourseID, 
    (scostu, course) 
=>   new  { StudentName  =  scostu.StudentName, CourseName  =  course.CourseName, ScoreValue  =  scostu.ScoreValue }); 


左外连接
当右侧的连接的右侧没有左侧对应的元素时,内连接会忽略左侧元素。要想保留左侧元素,可以使用做外连接。右侧被置为默认值,如:引用类型被置为空。
示例:

var  result  =  
    
from  student  in  DataSource.Students2 
    
join  score  in  DataSource.Scores  on  student.StudentID  equals  score.StudentID  into  Scores 
    
from  score  in  Scores.DefaultIfEmpty() 
    select 
new  { student  =  student, score  =  score  ==   default (Score)  ?   0  : score.Value }; 
//  result: 
//  { student = Student ID:5,Student Name:Erik, score = 78 } 
//  { student = Student ID:6,Student Name:Frank, score = 0 }  

等效的扩展方法调用实现为: 
var  result  =  
    DataSource.Students2.GroupJoin( 
    DataSource.Scores, 
    student 
=>  student.StudentID, 
    score 
=>  score.StudentID, 
    (student, Scores) 
=>   new  { student  =  student, Scores  =  Scores }) 
    .SelectMany(
group   =>   group .Scores.DefaultIfEmpty(), 
    (
group , score)  =>   new  { student  =   group .student, score  =  (score  ==   null ?   0.0  : score.Value });


笛卡尔积
集合中的元素交错连接。
示例:统计学生课程成绩时的模板。

var  result  =   from  student  in  DataSource.Students 
             
from  course  in  DataSource.Courses 
             select 
new  { StudentName  =  student.Name, CourseName  =  course.CourseName, ScoreValue  =  ( double ? ) null  }; 
//  result: 
//  { StudentName = Andy, CourseName = C Language, ScoreValue =  } 
//  { StudentName = Andy, CourseName = Biophysics, ScoreValue =  } 
//  ... 
//  { StudentName = Bill, CourseName = C Language, ScoreValue =  } 
//  ... 
//  { StudentName = Cindy, CourseName = Fundamentals of Compiling, ScoreValue =  } 
//  ...  

等效的扩展方法调用实现为: 
var  result  =  DataSource.Students.SelectMany( 
    student 
=>  DataSource.Courses 
        .Select( 
        course 
=>  
            
new  { StudentName  =  student.Name, CourseName  =  course.CourseName, ScoreValue  =  ( double ? ) null  }));


GroupJoin
连接分组。
方法原型为: 

public  static IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>( 
     this IEnumerable<TOuter> outer, 
    IEnumerable<TInner> inner, 
    Func<TOuter, TKey> outerKeySelector, 
    Func<TInner, TKey> innerKeySelector, 
    Func<TOuter, IEnumerable<TInner>, TResult> resultSelector) 

//  result: 
//  Andy 
//  1----78 
//  2----60 
//  ... 
//  Bill 
//  1----59 
//  ... 
//  Cindy 
//  2----60 
//  ...


相当于组合了Group操作和Join操作。等效的操作如下:

var result =  from item  in 
                 ( from student  in DataSource.Students 
                   join score  in DataSource.Scores  on student.StudentID  equals score.StudentID 
                  select  new { StudentName = student.Name, CourseID = score.CourseID, Value = score.Value }) 
              group item  by item.StudentName  into Group 
             select  new { StudentName = Group.Key, Group = Group };


结束语
到现在,Linq与SQL语言等价的操作基本介绍完,组合这些操作能实现复杂的查询。 

猜你喜欢

转载自sangei.iteye.com/blog/2324371