语言集成查询
时间:2023-12-21 11:18
语言集成查询(英语:Language Integrated Query,缩写:LINQ),发音"link",是微软的一项技术,新增一种自然查询的SQL语法到.NET Framework的编程语言中,目前可支持C#以及Visual Basic .NET语言。2007年11月19日随.NET Framework 3.5发布了LINQ技术。
包括LINQ to Objects、LINQ to SQL、LINQ to Datasets、LINQ to Entities、LINQ to Data Source、LINQ to XML/XSD等。
语言风格
LINQ依赖于语言的多项新增风格,来展示出查询语言的扩展性。例如:C#:
匿名类型
匿名类型(Anonymous type)是C# 3.0与Visual Basic 9.0新增的功能,它允许开发人员可以使用不具类型的方式建立新的数据结构,而真正的类型在编译时期,由C# (或VB) Compiler自动产生,并写入编译目标文件中,它可以让开发人员能够很简单利用匿名类型建立物件,LINQ中的select指令即是利用这种特性来建立回传物件。
匿名类型本质上是表达元组(tuple),采用值语义。
下列使用匿名类型的代码:
[WebGet] public IQueryable<Categories> GetCategoryByName(string CategoryName) { try { var query = base.CurrentDataSource.Categories.Where ("it.CategoryName = @Name", new ObjectParameter[] { new ObjectParameter("Name", CategoryName) }); } catch (Exception) { throw; } return query; }
会由编译器改写为:
[WebGet] public IQueryable<Categories> GetCategoryByName(string CategoryName) { IQueryable<Categories> CS$1$0000; // 由編譯器改寫而成。 try { CS$1$0000 = base.CurrentDataSource.Categories.Where ("it.CategoryName = @Name", new ObjectParameter[] { new ObjectParameter("Name", CategoryName) }); } catch (Exception) { throw; } return CS$1$0000; }
扩展方法 (Extension method)
Lambda表达式 (Lambda expression)
表达式树 (Expression tree)
查询表达式语法
from RangeVariable in IEnumerable<T>或IQueryable<T>的Collection <Standard Query Operators> <lambda expression> <select or groupBy operator> <result formation>
流利语法
LINQ查询时有两种语法可供选择:查询表达式语法(Query Expression)和流利语法(Fluent Syntax)。前者使用查询运算符;后者利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式进行查询。CLR本身并不理解查询表达式语法,它只理解流利语法。编译器负责把查询表达式语法编译为流利语法。
以下是一个示例LINQ方法语法的查询,返回数组中的偶数:
int[] ints={1,2,3,4}; var result = ints.Where(p => p % 2 == 0).ToArray();
对比流利语法和C#的传统语法:
// extension methods make LINQ elegant IEnumerable<string> query = names .Where(n => n.Contains("a")) .OrderBy(n => n.Length) .Select(n => n.ToUpper()); // static methods lose query's fluency IEnumerable<string> query2 = Enumerable.Select( Enumerable.OrderBy( Enumerable.Where(names, n => n.Contains("a") ), n => n.Length ), n => n.ToUpper() );
标准查询运算符 (Standard query operators)
System.Linq.Enumerable静态类声明了一套标准查询操作符(Standard Query Operators,SQO)方法集合。基本语法如下:
using (var db = new EntityContext()) { var roles = from o in db.Users where o.Account == "Apollo" select o.Roles; … }
标准查询操作符和Lambda表达式的关系非常密切。编译器会将上述表达式转化为下述以Lambda表达式为参数的显式扩展方法调用序列:
using (var db = new EntityContext()) { var roles = db.Users.Where(o => o.Account == "Apollo").Select(o => o.Roles); }
操作符 | 类别 | 语义 | 流利语法示例 | 查询表达式语法示例 |
---|---|---|---|---|
Where | 筛选操作符(Restriction) | Predicate→bool | var user = db.Users.Where( o => o.Roles != null); |
var users = from o in db.Users where o.Roles != null select o; |
Select | 投影操作符(Projection) | 将对象投影为一个匿名类型实例 TSource→TResult |
var users = db.Users.Select (o => new { o.Account, o.Password }); |
var users = from o in db.Users select new { o.Account, o.Password }; |
SelectMany | 投影操作符(Projection) | 返回多行结果,用于多表的交叉连接(cross join) | Dim res = Employees.SelectMany(Function(e) e.Family.Select(Function(c)c.name)) | |
Skip | 分块操作符(Partitioning) | 跳过前n个元素 | var users = db.Users.OrderBy( o => o.Roles.Count ).Skip(10); |
|
SkipWhile | 分块操作符(Partitioning) | 跳过起始处使条件为真的所有元素 | var users = db.Users.OrderBy( o => o.Roles.Count).SkipWhile( o => o.Roles == 3); |
|
Take | 分块操作符(Partitioning) | 返回开头之处的n个元素 | var users = db.Users.OrderBy( o => o.Roles.Count).Take(5); |
|
TakeWhile | 分块操作符(Partitioning) | 返回起始处使条件为真的所有元素 | var users = db.Users.OrderBy( o => o.Roles.Count).TakeWhile( o => o.Roles.Count == 3); |
|
Join | 连接操作符 | 内连接两个或多个表,仅限于Equals运算符 | var categoriesProducts = from c in nWEntities.Categories join p in nWEntities.Products on c.CategoryID equals p.CategoryID into productsByCategoryID select new { c.CategoryName, productCount = productsByCategoryID.Count() }; |
|
GroupJoin | 连接操作符 | 类似于LEFT OUTER JOIN,右侧集合匹配于左侧集合键值的元素被分组 | From cust In customers Group Join ord In orders On cust.CustomerID Equals ord.CustomerID Into CustomerOrders = Group, OrderTotal = Sum(ord.Total) | |
Concat | 合并操作符 | 用于连接两个序列 | returnValue = firstSeq.Concat(secondSeq) | |
OrderBy | 排序操作符(Ordering) | 升序排列 TSource→TKey |
var users = db.Users.OrderBy( o => o.Roles.Count); |
var users = from o in db.Users orderby o.Roles.Count select o; |
OrderByDescending | 排序操作符(Ordering) | 降序排列 | var users = db.Users.OrderByDescending( o => o.Roles.Count); |
var users = from o in db.Users orderby o.Roles.Count descending select o; |
ThenBy | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
ThenByDescending | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
Reverse | 排序操作符(Ordering) | 只能对IOrderedEnumerable接口对象使用 | ||
GroupBy | 分组操作符 | var users = db.Users.GroupBy( o => o.Roles.Count); |
var users = from o in db.Users group o by o.Roles.Count into g select new { RoleCount = g.Key, Group = g }; | |
Distinct | 集合操作符 | 去重复 | var roles = user.Roles.Distinct(); |
|
Union | 集合操作符 | 集合并,还去重复 | var roles = user1.Roles.Union( user2.Roles); |
|
Intersect | 集合操作符 | 集合交 | var roles = user1.Roles.Intersect( user2.Roles); |
|
Except | 集合操作符 | 集合差 | var roles = user1.Roles.Except( user2.Roles); |
|
AsEnumerable | 转换操作符 | 用于把一个IEnumerable的派生类型转化为IEnumerable类型 | ||
AsQueryable | 转换操作符 | IEnumerable(Of T)转化为IQueryable(Of T). | ||
ToArray | 转换操作符 | 转换为数组 | ||
ToList | 转换操作符 | 转换为List | ||
ToDictionary | 转换操作符 | 转换为一对一的字典(键-值对的集合) | ||
ToLookup | 转换操作符 | 转换为一对多的字典(键-值集的集合) | ||
OfType | 转换操作符 | 获取指定类型的元素组成一个数组 | object[] numbers = { null, 1.0, "two", 3, "four", 5, "six", 7.0 }; var doubles = numbers.OfType<double>(); | |
Cast | 转换操作符 | 把序列的所有元素转换为指定类型 | ||
SequenceEqual | 相等操作符 | 两个序列的元素依次相同返回真。 使用元素所属类的IEqualityComparer(Of T) 泛型界面做相等比较 |
||
First | 元素操作符 | 返回序列第一个元素(或满足条件第一个元素),没有则异常 | ||
FirstOrDefault | 元素操作符 | 返回序列第一个元素,没有则返回空或默认值 | var user = db.Users.FirstOrDefault( o => o.Roles.Count == 3); |
|
Last | 元素操作符 | 返回序列最后一个元素,没有则异常 | ||
LastOrDefault | 元素操作符 | 返回序列最后一个元素,没有则返回空或默认值 | var user = db.Users.LastOrDefault( o => o.Roles.Count == 3); |
|
Single | 元素操作符 | 返回序列唯一元素,如果没有元素或多个元素则异常 | ||
SingleOrDefault | 元素操作符 | 返回序列唯一元素,如果多个元素则异常 | var user = db.Users.SingleOrDefault(o => o.Account == "Apollo"); |
|
ElementAt | 元素操作符 | 返回序列指定元素,失败则异常 | ||
ElementAtOrDefault | 元素操作符 | 返回序列指定元素,失败则空或默认值 | ||
DefaultIfEmpty | 元素操作符 | 返回序列,如果序列为空则返回元素的默认值 | For Each number As Integer In numbers.DefaultIfEmpty() output.AppendLine(number) Next | |
All | 量词操作符 | 序列所有元素满足条件则为真 | var result = db.Users.All( o => o.Roles.Count == 3); |
|
Any | 量词操作符 | 序列有一个元素满足条件则为真 | var result = db.Users.Any( o => o.Roles.Count == 3); |
|
Contains | 量词操作符 | 是否包含一个元素 | var result = db.Users.Where( o => o.Roles.Count == 3 ).Contains(user1); |
|
Count | 聚合统计操作符 | 计数,可选一个谓词 | var result = db.Users.Count (o => o.Roles.Count == 3); |
|
LongCount | 聚合统计操作符 | 计数,返回Int64类型 | ||
Sum | 聚合统计操作符 | 求和,可选对一个lambda函数表达式 | var result = db.Users.Sum (o => o.Roles.Count); |
|
Min | 聚合统计操作符 | 最小值,可选对一个lambda函数表达式 | var result = db.Users.Min (o => o.Roles.Count); |
|
Max | 聚合统计操作符 | var result = db.Users.Max (o => o.Roles.Count); |
||
Average | 聚合统计操作符 | var result = db.Users.Average (o => o.Roles.Count); |
||
Aggregate | 聚合统计操作符 | 参数为一个委托,在序列的每个元素上执行该委托。 委托的第一个参数为当前累计值,第二个参数为当前元素, 返回值为新的累计值 |
Dim reversed As String = words.Aggregate( Function(ByVal current, ByVal word) word & " " & current) | |
equals/Equals | 关键字 | 用于Join子句 | ||
from/From | 关键字 | |||
in/In | 关键字 | 指出数据源 | ||
into/Into | 关键字 | 用于Group By子句 | ||
key | 关键字 | 用于Group By子句的无名类型 | ||
let | 关键字 | 给表达式定义别名 | From prod In products Let Discount = prod.UnitPrice * 0.1 Where Discount >= 50 select prod.ProductName, prod.UnitPrice, Discount | |
Group | 关键字 | 在GroupBy子句的Into中用于辨识分组结果 | From num In numbers Group num By remainder5 = (num Mod 5) Into Group | |
Range | 方法 | 产生一个整数序列 | From n In Enumerable.Range(100, 50) | |
Repeat | 方法 | 产生一个整数序列 | From n In Enumerable.Repeat(7, 10) |
LINQ的各式言语支持度
下列的言语支持LINQ。
- C# 3.0
- F# 1.1.8.1
- Visual Basic 2008(9.0)
注:C++/CLI尚未支持LINQ。但是有第三方的C++包,以及第三方的PHP包
LINQ的示例
一个简单例子:
using System; using System.Linq; namespace DuckTyping { internal class Program { private static void Main() { int[] array = { 1, 5, 2, 10, 7 }; // select squares of all odd numbers in the array sorted in descending order var results = from x in array where x % 2 == 1 orderby x descending select x * x; foreach (var result in results) { Console.WriteLine(result); } } } }
输出: 49 25 1
另一个例子:
// the Northwind type is a subclass of DataContext created by SQLMetal // Northwind.Orders is of type Table<Order> // Northwind.Customers is of type Table<Customer> Northwind db = new Northwind(connectionString); // use 'var' keyword because there is no name for the resultant type of the projection var q = from o in db.Orders from c in db.Customers where o.Quality == "200" && (o.CustomerID == c.CustomerID) select new { o.DueDate, c.CompanyName, c.ItemID, c.ItemName }; // q is now an IEnumerable<T>, where T is the anonymous type generated by the compiler foreach (var t in q) { // t is strongly typed, even if we can't name the type at design time Console.WriteLine("DueDate Type = {0}", t.DueDate.GetType()); Console.WriteLine("CompanyName (lowercased) = {0}", t.CompanyName.ToLower()); Console.WriteLine("ItemID * 2 = {0}", t.ItemID * 2); }
Visual Studio支持
LINQ目前由Visual Studio 2008、2010、2012、2013、2015、2017、2019支持。
语言扩展
微软同样提供了LINQExtender,允许用户在不了解LINQ实现细节的情况下,编写自己的LINQ扩展。 如:LINQ to Twitter,LINQ to Oracle,LINQ to Active Directory等
为您推荐:
- WinJS 2023-12-21
- Autocommit 2023-12-21
- DirectAccess 2023-12-21
- 交易者 2023-12-21
- 新加坡裔澳大利亚人 2023-12-21
- 铁拳7 2023-12-21