语言集成查询 (LINQ) 不只是检索数据。 它也是用于转换数据的强大工具。 通过使用 LINQ查询,可以使用源序列作为输入,并通过多种方式对其进行修改,以创建新的输出序列。通过排序和分组,你可以修改序列本身,而无需修改这些元素本身。 但也许 LINQ 查询最强大的功能是创建新类型。 这可以在 select 子句中完成。 例如,可以执行下列任务:
这只是几个例子。 当然,可以以各种方式在同一查询中组合这些转换。 此外,一个查询的输出序列可以用作新查询的输入序列。
将多个输入联接到一个输出序列中
可以使用 LINQ 查询创建包含元素的输出序列,这些元素来自多个输入序列。 以下示例演示如何组合两个内存中数据结构,但相同的原则可应用于组合来自 XML 或 SQL 或数据集源的数据。 假设以下两种类类型:
class Student
{
public string First { get; set; }
public string Last {get; set;}
public int ID { get; set; }
public string Street { get; set; }
public string City { get; set; }
public List<int> Scores;
}
class Teacher
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public string City { get; set; }
}
以下示例演示了查询:
1 class DataTransformations
2 {
3 static void Main()
4 {
5 // 创建第一个数据源
6 List<Student> students = new List<Student>()
7 {
8 new Student { First="Svetlana",
9 Last="Omelchenko",
10 ID=111,
11 Street="123 Main Street",
12 City="Seattle",
13 Scores= new List<int> { 97, 92, 81, 60 } },
14 new Student { First="Claire",
15 Last="O’Donnell",
16 ID=112,
17 Street="124 Main Street",
18 City="Redmond",
19 Scores= new List<int> { 75, 84, 91, 39 } },
20 new Student { First="Sven",
21 Last="Mortensen",
22 ID=113,
23 Street="125 Main Street",
24 City="Lake City",
25 Scores= new List<int> { 88, 94, 65, 91 } },
26 };
27
28 // 创建第二个数据源
29 List<Teacher> teachers = new List<Teacher>()
30 {
31 new Teacher { First="Ann", Last="Beebe", ID=945, City="Seattle" },
32 new Teacher { First="Alex", Last="Robinson", ID=956, City="Redmond" },
33 new Teacher { First="Michiyo", Last="Sato", ID=972, City="Tacoma" }
34 };
35
36 // 创建查询
37 var peopleInSeattle = (from student in students
38 where student.City == "Seattle"
39 select student.Last)
40 .Concat(from teacher in teachers
41 where teacher.City == "Seattle"
42 select teacher.Last);
43
44 Console.WriteLine("The following students and teachers live in Seattle:");
45 // 执行查询
46 foreach (var person in peopleInSeattle)
47 {
48 Console.WriteLine(person);
49 }
50
51 Console.WriteLine("Press any key to exit.");
52 Console.ReadKey();
53 }
54 }
55 /* 输出:
56 The following students and teachers live in Seattle:
57 Omelchenko
58 Beebe
59 */
有关详细信息,请参阅 join 子句和 select 子句。
选择每个源元素的子集
有两种主要方法来选择源序列中每个元素的子集:
Customer
对象包含多个公共属性,包括名为 City
的字符串。 在执行时,此查询将生成字符串的输出序列。
var query = from cust in Customers
select cust.City;
Customer
元素的两个属性:
var query = from cust in Customer
select new {Name = cust.Name, City = cust.City};
有关详细信息,请参阅对象和集合初始值设定项和匿名类型。
将内存中对象转换为 XML
LINQ 查询可以轻松地在内存中数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。 以下示例将内存中数据结构中的对象转换为 XML 元素。
1 class XMLTransform
2 {
3 static void Main()
4 {
5 // 使用集合初始值设定项创建数据源
6 // 学生类是在上述定义的
7 List<Student> students = new List<Student>()
8 {
9 new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List<int>{97, 92, 81, 60}},
10 new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List<int>{75, 84, 91, 39}},
11 new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List<int>{88, 94, 65, 91}},
12 };
13
14 // 创建查询
15 var studentsToXML = new XElement("Root",
16 from student in students
17 let scores = string.Join(",", student.Scores)
18 select new XElement("student",
19 new XElement("First", student.First),
20 new XElement("Last", student.Last),
21 new XElement("Scores", scores)
22 ) // end "student"
23 ); // end "Root"
24
25 // 执行查询
26 Console.WriteLine(studentsToXML);
27
28 // Keep the console open in debug mode.
29 Console.WriteLine("Press any key to exit.");
30 Console.ReadKey();
31 }
32 }
此代码生成以下 XML 输出:
<Root>
<student>
<First>Svetlana</First>
<Last>Omelchenko</Last>
<Scores>97,92,81,60</Scores>
</student>
<student>
<First>Claire</First>
<Last>O'Donnell</Last>
<Scores>75,84,91,39</Scores>
</student>
<student>
<First>Sven</First>
<Last>Mortensen</Last>
<Scores>88,94,65,91</Scores>
</student>
</Root>
有关详细信息,请参阅在 C# 中创建 XML 树 (LINQ to XML)。
对源元素执行操作
输出序列可能不包含源序列中的任何元素或元素属性。 输出可能是使用源元素作为输入参数而计算得出的值序列。 以下简单查询在执行时会输出一串字符串,其值表示基于 double
类型的元素的源序列的计算结果。
如果查询将被转换为另一个域,则不支持在查询表达式中调用方法。 例如,不能在 LINQ to SQL 中调用普通的 C# 方法,因为 SQL Server 没有用于它的上下文。 但是,可以将存储过程映射到方法并调用这些方法。 有关详细信息,请参阅存储过程。
class FormatQuery
{
static void Main()
{
// 数据源
double[] radii = { 1, 2, 3 };
// 查询表达式
IEnumerable<string> query =
from rad in radii
select $"Area = {rad * rad * Math.PI:F2}";
// 执行查询
foreach (string s in query)
Console.WriteLine(s);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 输出:
Area = 3.14
Area = 12.57
Area = 28.27
*/