本文讲述了linq查询常用的from/where/orderby/groupby/join/where字句的用法。
获取数据源&from
在 LINQ 查询中,第一步是指定数据源。 像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用它。 在 LINQ 查询中,最先使用 from 子句的目的是引入数据源 (customers) 和范围变量 (cust)。
C#
//queryAllCustomers is an IEnumerable<Customer>
var queryAllCustomers = from cust in customers
select cust;
范围变量类似于 foreach 循环中的迭代变量,但在查询表达式中,实际上不发生迭代。 执行查询时,范围变量将用作对 customers 中的每个后续元素的引用。 因为编译器可以推断 cust 的类型,所以您不必显式指定此类型。
筛选&where
也许最常用的查询操作是应用布尔表达式形式的筛选器。 此筛选器使查询只返回那些表达式结果为 true 的元素。 使用 where 子句生成结果。 实际上,筛选器指定从源序列中排除哪些元素。 在下面的示例中,只返回那些地址位于伦敦的 customers。
C#
var queryLondonCustomers = from cust in customers
where cust.City == "London"
select cust;
您可以使用熟悉的 C# 逻辑 AND 和 OR 运算符来根据需要在 where 子句中应用任意数量的筛选表达式。例如,若要只返回位于“伦敦”AND 姓名为“Devon”的客户,您应编写下面的代码:
C#
where cust.City=="London" && cust.Name == "Devon"
若要返回位于伦敦或巴黎的客户,您应编写下面的代码:
C#
where cust.City == "London" || cust.City == "Paris"
Order by
通常可以很方便地将返回的数据进行排序。 orderby 子句将使返回的序列中的元素按照被排序的类型的默认比较器进行排序。 例如,下面的查询可以扩展为按 Name 属性对结果进行排序。 因为 Name 是一个字符串,所以默认比较器执行从 A 到 Z 的字母排序。
C#
var queryLondonCustomers3 =
from cust in customers
where cust.City == "London"
orderby cust.Name ascending
select cust;
若要按相反顺序(从 Z 到 A)对结果进行排序,请使用 orderby…descending 子句。
分组&group by
使用 group 子句,您可以按指定的键分组结果。 例如,您可以指定结果应按 City 分组,以便位于伦敦或巴黎的所有客户位于各自组中。 在本例中,cust.City 是键。
C#
// queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>
var queryCustomersByCity =
from cust in customers
group cust by cust.City;
// customerGroup is an IGrouping<string, Customer>
foreach (var customerGroup in queryCustomersByCity)
{
Console.WriteLine(customerGroup.Key);
foreach (Customer customer in customerGroup)
{
Console.WriteLine(" {0}", customer.Name);
}
}
在使用 group 子句结束查询时,结果采用列表的列表形式。 列表中的每个元素是一个具有 Key 成员及根据该键分组的元素列表的对象。 在循环访问生成组序列的查询时,您必须使用嵌套的 foreach 循环。 外部循环用于循环访问每个组,内部循环用于循环访问每个组的成员。
【如果您必须引用组操作的结果,可以使用 into 关键字来创建可进一步查询的标识符。 下面的查询只返回那些包含两个以上的客户的组:
C#
// custQuery is an IEnumerable<IGrouping<string, Customer>>
var custQuery =
from cust in customers
group cust by cust.City into custGroup
where custGroup.Count() > 2
orderby custGroup.Key
select custGroup;
】
联接 jion
联接运算创建数据源中没有显式建模的序列之间的关联。 例如,您可以执行联接来查找位于同一地点的所有客户和经销商。 在 LINQ 中,join 子句始终针对对象集合而非直接针对数据库表运行。
有关更多信息,请参见join 子句(C# 参考)。
选择(投影)&select 子句
在查询表达式中,select 子句可以指定将在执行查询时产生的值的类型。 该子句的结果将基于前面所有子句的计算结果以及 select 子句本身中的所有表达式。 查询表达式必须以 select 子句或 group 子句结束。
下面的示例演示了查询表达式中的简单 select 子句。
C#
class SelectSample1
{
static void Main()
{
//Create the datasource
List<int> Scores = newList<int>() { 97, 92, 81, 60 };
// Create the query.
IEnumerable<int> queryHighScores =
from score inScores
where score > 80
select score;
// Execute the query.
foreach (int i in queryHighScores)
{
Console.Write(i + "");
}
}
}
在最简单的情况下,select 子句仅指定范围变量,这会使返回的序列包含与数据源具有相同类型的元素。不过,select 子句还提供了一种功能强大的机制,可用于将源数据转换(或投影)为新类型。下面的示例演示了 select 子句可能采用的所有不同形式。 在每个查询中,请注意 select 子句和查询变量(studentQuery1、studentQuery2 等)的类型之间的关系。
C#
class SelectSample2
{
// Define someclasses
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public List<int>Scores;
public ContactInfo GetContactInfo(SelectSample2app, int id)
{
ContactInfo cInfo =
(from ci in app.contactList
where ci.ID== id
select ci)
.FirstOrDefault();
returncInfo;
}
public override string ToString()
{
return First+ " " + Last + ":" + ID;
}
}
public class ContactInfo
{
public int ID { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public override string ToString() { return Email+ "," + Phone; }
}
public class ScoreInfo
{
public double Average { get; set; }
public int ID { get; set; }
}
// The primary datasource
List<Student> students = new List<Student>()
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= newList<int>() {97, 92, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= newList<int>() {75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= newList<int>() {88, 94, 65, 91}},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= newList<int>() {97, 89, 85, 82}},
};
// Separate datasource for contact info.
List<ContactInfo> contactList = new List<ContactInfo>()
{
new ContactInfo {ID=111, Email="SvetlanO@Contoso.com", Phone="206-555-0108"},
new ContactInfo {ID=112, Email="ClaireO@Contoso.com", Phone="206-555-0298"},
newContactInfo {ID=113, Email="SvenMort@Contoso.com", Phone="206-555-1130"},
new ContactInfo {ID=114, Email="CesarGar@Contoso.com", Phone="206-555-0521"}
};
static void Main(string[]args)
{
SelectSample2 app = new SelectSample2();
// Produce a filteredsequence of unmodified Students.
IEnumerable<Student> studentQuery1 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent;
Console.WriteLine("Query1:select range_variable");
foreach (Student s instudentQuery1)
{
Console.WriteLine(s.ToString());
}
// Produce a filteredsequence of elements that contain
// only one propertyof each Student.
IEnumerable<String> studentQuery2 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent.Last;
Console.WriteLine("\r\nstudentQuery2: select range_variable.Property");
foreach (string s in studentQuery2)
{
Console.WriteLine(s);
}
// Produce a filteredsequence of objects created by
// a method call on each Student.
IEnumerable<ContactInfo> studentQuery3 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent.GetContactInfo(app, student.ID);
Console.WriteLine("\r\nstudentQuery3: select range_variable.Method");
foreach (ContactInfo ci instudentQuery3)
{
Console.WriteLine(ci.ToString());
}
// Produce a filteredsequence of ints from
// the internal arrayinside each Student.
IEnumerable<int> studentQuery4 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent.Scores[0];
Console.WriteLine("\r\nstudentQuery4: select range_variable[index]");
foreach (int i in studentQuery4)
{
Console.WriteLine("First score = {0}", i);
}
// Produce a filteredsequence of doubles
// that are theresult of an expression.
IEnumerable<double> studentQuery5 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent.Scores[0] * 1.1;
Console.WriteLine("\r\nstudentQuery5: select expression");
foreach (double d in studentQuery5)
{
Console.WriteLine("Adjusted first score = {0}", d);
}
// Produce a filteredsequence of doubles that are
// the result of a method call.
IEnumerable<double> studentQuery6 =
fromstudent in app.students
wherestudent.ID > 111
selectstudent.Scores.Average();
Console.WriteLine("\r\nstudentQuery6: select expression2");
foreach (double d in studentQuery6)
{
Console.WriteLine("Average = {0}", d);
}
// Produce a filteredsequence of anonymous types
// that contain onlytwo properties from each Student.
var studentQuery7 =
fromstudent in app.students
wherestudent.ID > 111
select new { student.First, student.Last };
Console.WriteLine("\r\nstudentQuery7: select new anonymous type");
foreach (var item instudentQuery7)
{
Console.WriteLine("{0}, {1}", item.Last, item.First);
}
// Produce a filteredsequence of named objects that contain
// a method returnvalue and a property from each Student.
// Use named types ifyou need to pass the query variable
// across a methodboundary.
IEnumerable<ScoreInfo> studentQuery8 =
fromstudent in app.students
wherestudent.ID > 111
select new ScoreInfo
{
Average =student.Scores.Average(),
ID = student.ID
};
Console.WriteLine("\r\n studentQuery8: select new named type");
foreach (ScoreInfo si instudentQuery8)
{
Console.WriteLine("ID = {0}, Average = {1}", si.ID, si.Average);
}
// Produce a filteredsequence of students who appear on a contact list
// and whose averageis greater than 85.
IEnumerable<ContactInfo> studentQuery9 =
fromstudent in app.students
wherestudent.Scores.Average() > 85
join ci in app.contactList on student.ID equals ci.ID
select ci;
Console.WriteLine("\r\nstudentQuery9: select result of join clause");
foreach (ContactInfo ci instudentQuery9)
{
Console.WriteLine("ID = {0}, Email = {1}", ci.ID, ci.Email);
}
// Keep the consolewindow open in debug mode
Console.WriteLine("Pressany key to exit.");
Console.ReadKey();
}
}
/* Output
Query1: select range_variable
Claire O'Donnell:112
Sven Mortensen:113
Cesar Garcia:114
studentQuery2: select range_variable.Property
O'Donnell
Mortensen
Garcia
studentQuery3: select range_variable.Method
ClaireO@Contoso.com,206-555-0298
SvenMort@Contoso.com,206-555-1130
CesarGar@Contoso.com,206-555-0521
studentQuery4: select range_variable[index]
First score = 75
First score = 88
First score = 97
studentQuery5: select expression
Adjusted first score = 82.5
Adjusted first score = 96.8
Adjusted first score = 106.7
studentQuery6: select expression2
Average = 72.25
Average = 84.5
Average = 88.25
studentQuery7: select new anonymous type
O'Donnell, Claire
Mortensen, Sven
Garcia, Cesar
studentQuery8: select new named type
ID = 112, Average = 72.25
ID = 113, Average = 84.5
ID = 114, Average = 88.25
studentQuery9: select result of join clause
ID = 114, Email = CesarGar@Contoso.com
*/
如上一个示例中的 studentQuery8 所示,您有时可能希望所返回序列中的元素仅包含源元素的属性子集。 通过使返回的序列尽可能地小一些,可以降低内存需求,并提高查询的执行速度。 通过在 select 子句中创建一个匿名类型,并且借助于对象初始值设定项用源元素中的适当属性对该匿名类型进行初始化,可以达到此目的。