本示例演示如何将来自不同源的数据合并到一系列新的类型。
请勿尝试将内存中数据或文件系统中的数据与仍在数据库中的数据进行联接。 这种跨域联接可能产生未定义的结果,因为可能为数据库查询和其他类型的源定义了联接操作的不同方式。 此外,如果数据库中的数据量足够大,这样的操作还存在可能导致内存不足的异常的风险。 若要将数据库中的数据联接到内存数据,首先对数据库查询调用
ToList
或ToArray
,然后对返回的集合执行联接。
此示例使用两个文件。 第一个 names.csv 文件包含学生名称和学生 ID。
Omelchenko,Svetlana,111
O'Donnell,Claire,112
Mortensen,Sven,113
Garcia,Cesar,114
Garcia,Debra,115
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Hugo,118
Tucker,Lance,119
Adams,Terry,120
Zabokritski,Eugene,121
Tucker,Michael,122
第二个 scores.csv 文件包含第一列中的学生 ID,后跟考试分数。
111, 97, 92, 81, 60
112, 75, 84, 91, 39
113, 88, 94, 65, 91
114, 97, 89, 85, 82
115, 35, 72, 91, 70
116, 99, 86, 90, 94
117, 93, 92, 80, 87
118, 92, 90, 83, 78
119, 68, 79, 88, 92
120, 99, 82, 81, 79
121, 96, 85, 91, 60
122, 94, 92, 91, 91
下面的示例演示如何使用命名记录 Student
存储来自两个内存字符串集合(模拟 .csv 格式的电子表格数据)的合并数据。 该 ID 用作将学生映射到其分数的键。
// Each line of names.csv consists of a last name, a first name, and an
// ID number, separated by commas. For example, Omelchenko,Svetlana,111
string[] names = File.ReadAllLines("names.csv");// Each line of scores.csv consists of an ID number and four test
// scores, separated by commas. For example, 111, 97, 92, 81, 60
string[] scores = File.ReadAllLines("scores.csv");// Merge the data sources using a named type.
// var could be used instead of an explicit type. Note the dynamic
// creation of a list of ints for the ExamScores member. The first item
// is skipped in the split string because it is the student ID,
// not an exam score.
IEnumerable<Student> queryNamesScores = from nameLine in nameslet splitName = nameLine.Split(',')from scoreLine in scoreslet splitScoreLine = scoreLine.Split(',')where Convert.ToInt32(splitName[2]) == Convert.ToInt32(splitScoreLine[0])select new Student(FirstName: splitName[0],LastName: splitName[1],ID: Convert.ToInt32(splitName[2]),ExamScores: (from scoreAsText in splitScoreLine.Skip(1)select Convert.ToInt32(scoreAsText)).ToArray());// Optional. Store the newly created student objects in memory
// for faster access in future queries. This could be useful with
// very large data files.
List<Student> students = queryNamesScores.ToList();// Display each student's name and exam score average.
foreach (var student in students)
{Console.WriteLine($"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}.");
}
/* Output:
The average score of Omelchenko Svetlana is 82.5.
The average score of O'Donnell Claire is 72.25.
The average score of Mortensen Sven is 84.5.
The average score of Garcia Cesar is 88.25.
The average score of Garcia Debra is 67.
The average score of Fakhouri Fadi is 92.25.
The average score of Feng Hanying is 88.
The average score of Garcia Hugo is 85.75.
The average score of Tucker Lance is 81.75.
The average score of Adams Terry is 85.25.
The average score of Zabokritski Eugene is 83.
The average score of Tucker Michael is 92.
*/
在“选择”子句中,将从两个源中的数据初始化每个新的 Student
对象。
如果不需要存储查询的结果,那么与命名的类型相比,元组或匿名类型使用起来更方便。 下面的示例执行与前面示例相同的任务,但使用的是元组,而不是命名的类型:
// Merge the data sources by using an anonymous type.
// Note the dynamic creation of a list of ints for the
// ExamScores member. We skip 1 because the first string
// in the array is the student ID, not an exam score.
var queryNamesScores2 = from nameLine in nameslet splitName = nameLine.Split(',')from scoreLine in scoreslet splitScoreLine = scoreLine.Split(',')where Convert.ToInt32(splitName[2]) == Convert.ToInt32(splitScoreLine[0])select (FirstName: splitName[0], LastName: splitName[1], ExamScores: (from scoreAsText in splitScoreLine.Skip(1)select Convert.ToInt32(scoreAsText)).ToList());// Display each student's name and exam score average.
foreach (var student in queryNamesScores2)
{Console.WriteLine($"The average score of {student.FirstName} {student.LastName} is {student.ExamScores.Average()}.");
}