查询是一种从数据源检索数据的表达式。 查询通常用专门的查询语言来表示。 随着时间的推移,人们已经为各种数据源开发了不同的语言;例如,用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此,开发人员不得不针对他们必须支持的每种数据源或数据格式而学习新的查询语言。 LINQ 通过提供一种跨各种数据源和数据格式使用数据的一致模型,简化了这一情况。 在 LINQ 查询中,始终会用到对象。 可以使用相同的基本编码模式来查询和转换 XML 文档、SQL 数据库、ADO.NET 数据集、.NET 集合中的数据以及对其有 LINQ 提供程序可用的任何其他格式的数据。
class IntroToLINQ { static void Main() { // The Three Parts of a LINQ Query: // 1. Data source. int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; // 2. Query creation. // numQuery is an IEnumerable<int> var numQuery = from num in numbers where (num % 2) == 0 select num; // 3. Query execution. foreach (int num in numQuery) { Console.Write("{0,1} ", num); } } }
在上一个示例中,由于数据源是数组,因此它隐式支持泛型 IEnumerable<T> 接口。这一事实意味着该数据源可以用 LINQ 进行查询。在 foreach 语句中执行查询,而foreach 要求使用 IEnumerable 或 IEnumerable<T>。支持 IEnumerable<T> 或派生接口(如泛型 IQueryable<T>)的类型称为“可查询类型”。可查询类型不需要进行修改或特殊处理就可以用作 LINQ 数据源。如果源数据还没有作为可查询类型出现在内存中,则 LINQ 提供程序必须以此方式表示源数据。例如,LINQ to XML 将 XML 文档加载到可查询的 XElement 类型中:
// Create a data source from an XML document. // using System.Xml.Linq; XElement contacts = XElement.Load(@"c:\myContactList.xml");
在 LINQ to SQL 中,首先手动或使用 对象关系设计器(O/R 设计器) 在设计时创建对象关系映射。针对这些对象编写查询,然后由 LINQ to SQL 在运行时处理与数据库的通信。在下面的示例中,Customers 表示数据库中的特定表,并且查询结果的类型 IQueryable<T> 派生自 IEnumerable<T>。
// Query for customers in London. IQueryable<Customer> custQuery = from cust in db.Customers where cust.City == "London" select cust;
LINQ 数据源是支持泛型 IEnumerable<T> 接口或从该接口继承的接口的任意对象。
延迟执行
LINQ 查询变量类型化为 IEnumerable<T> 或派生类型,如 IQueryab
如前所述,查询变量本身只是存储查询命令。实际的查询执行会延迟到在 foreach 语句中循环访问查询变量时发生。此概念称为“延迟执行”,下面的示例对此进行了演示:
// Query execution. foreach (int num in numQuery) { Console.Write("{0,1} ", num); }
foreach 语句也是检索查询结果的地方。例如,在上一个查询中,迭代变量 num 保存了返回的序列中的每个值(一次保存一个值)。由于查询变量本身从不保存查询结果,因此可以根据需要随意执行查询。例如,可以通过一个单独的应用程序持续更新数据库。在应用程序中,可以创建一个检索最新数据的查询,并可以按某一时间间隔反复执行该查询以便每次检索不同的结果。
强制立即执行
对一系列源元素执行聚合函数的查询必须首先循环访问这些元素。 Count、Max、Average 和 First 就属于此类查询。由于查询本身必须使用 foreach 以便返回结果,因此这些查询在执行时不使用显式 foreach 语句。另外还要注意,这些类型的查询返回单个值,而不是 IEnumerable 集合。下面的查询返回源数组中偶数的计数:
var evenNumQuery = from num in numbers where (num % 2) == 0 select num; int evenNumCount = evenNumQuery.Count();
若要强制立即执行任意查询并缓存其结果,可以调用 ToList<TSource> 或 ToArray<TSource> 方法。
List<int> numQuery2 = (from num in numbers where (num % 2) == 0 select num).ToList(); // or like this: // numQuery3 is still an int[] var numQuery3 = (from num in numbers where (num % 2) == 0 select num).ToArray();
LINQ 查询基于泛型类型,在 .NET Framework 的 2.0 版中引入了泛型类型。 您无需深入了解泛型即可开始编写查询。 但是,您可能需要了解两个基本概念:
-
当您创建泛型集合类(如 List<T>)的实例时,您将“T”替换为列表将包含的对象的类型。 例如,字符串列表表示为 List<string>,Customer 对象列表表示为List<Customer>。 泛型列表是强类型的,且提供了比将其元素存储为 Object 的集合更多的好处。 如果您尝试将 Customer 添加到 List<string>,则会在编译时出现一条错误。 泛型集合易于使用的原因是您不必执行运行时类型强制转换。
-
IEnumerable<T> 是一个接口,通过该接口,可以使用 foreach 语句来枚举泛型集合类。 泛型集合类支持 IEnumerable<T>,就像非泛型集合类(如 ArrayList)支持IEnumerable。
IEnumerable<Customer> customerQuery = from cust in customers where cust.City == "London" select cust; foreach (Customer customer in customerQuery) { Console.WriteLine(customer.LastName + ", " + customer.FirstName); }
var customerQuery2 = from cust in customers where cust.City == "London" select cust; foreach(var customer in customerQuery2) { Console.WriteLine(customer.LastName + ", " + customer.FirstName); }
当变量的类型明显或显式指定嵌套泛型类型(如由组查询生成的那些类型)并不重要时,var 关键字很有用。通常,我们建议如果您使用 var,应意识到这可能使您的代码更难以让别人理解。