本篇介紹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語言等價的操作基本介紹完,組合這些操作能實現復雜的查詢。