共找到2條詞條名為LINQ的結果 展開

LINQ

語言集成查詢

LINQ(Language Integrated Query)語言集成查詢是一組用於c#和Visual Basic語言的擴展。它允許編寫C#或者Visual Basic代碼以操作內存數據的方式,查詢資料庫。包括LINQ to Objects、LINQ to SQL、LINQ to Datasets、LINQ to Entities、LINQ to Data Source、LINQ to XML/XSD等。

基本含義


從技術角度而言,LINQ定義了大約40個查詢操作符,如select、from、in、where以及order by(C#中)。使用這些操作符可以編寫查詢語句。不過,這些查詢還可以基於很多類型的數據,每個數據類型都需要一個單獨的LINQ類型。
經過了最近 20 年,面向對象編程技術( object-oriented (OO) programming technologies )在工業領域的應用已經進入了一個穩定的發展階段。程序員現在都已經認同像 類(classes)、對象(objects)、方法(methods)這樣的語言特性。考察現在和下一代的技術,一個新的編程技術的重大挑戰開始呈現出來,即面向對象技術誕生以來並沒有解決降低訪問和整合信息數據( accessing and integrating information )的複雜度的問題。其中兩個最主要訪問的數據源與資料庫(database)和XML(標準通用標記語言下的一個應用)相關。
LINQ 提供了一條更常規的途徑即給.Net Framework添加一些可以應用於所有信息源( all sources of information )的具有多種用途( general-purpose )的語法查詢特性( query facilities ),這是比向開發語言和運行時( runtime )添加一些關係數據( relational )特性或者類似 XML 特性( XML-specific )更好的方式。這些語法特性就叫做 .NET Language Integrated Query (LINQ) 。
包含 DLinq 和 XLinq。

概念


LINQ的讀法:lin k 很多人會誤讀為lin Q
LINQ的全稱:Language-Integrated Query
LINQ的關鍵詞:from, select, in, where, group by, orderby, …
LINQ的寫法:
1) from 臨時變數 in 實現IEnumerable介面的對象
[orderby 條件]
[group by 條件]
select 臨時變數中被查詢的值
2) 實現IEnumerable介面的對象.LINQ方法名(lambda表達式)。如:
string input = "hello world";
int count = input.Count(w=>w == 'o'); //查詢字母o出現的次數
注意:能夠使用LINQ的對象需要實現IEnumerable介面。並且LINQ的查詢表達式是在最近一次創建對 象時才被編譯的。
5. 命名空間(.NET Framework):System.Linq;
注意:Linq是在.NET Framework 3.5 中出現的技術,所以在創建新項目的時候必須要選3.5或者更高版 本,否則無法使用。選擇3.5或更高版本的.NET Framework之後,創建類文件中會自動包含System.Linq 的命名空間。

語言風格


LINQ新增了多項語言的風格,來展示出查詢語言的擴展性,例如:C#:
匿名類型
匿名類型(Anonymous type)是C# 3.0與Visual Basic 9.0新增的功能,它允許開發人員可以使用不具類型的方式創建新的數據結構,而真正的類型在編譯時期,由C# (或VB) Compiler自動產生,並寫入編譯目標文件中,它可以讓開發人員能夠很簡單利用匿名類型創建對象,LINQ中的select指令即是利用這種特性來創建回傳對象。
匿名類型本質上是表達元組(tuple),採用值語義 。
下列使用匿名類型的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[WebGet]
public IQueryable
GetCategoryByName(string CategoryName) {
try{
 var query = base.CurrentDataSource.Categories.Where ("it.CategoryName = @Name", new ObjectParameter[] 
{
new ObjectParameter("Name", CategoryName) });
 }
catch (Exception)
 {
throw;
 }
return query;
 }
會由編譯器改寫為:
1
2
3
4
5
6
7
8
9
10
11
12
[WebGet]
public IQueryable 
GetCategoryByName(string CategoryName) {
IQueryable 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;
}
標準查詢運算符
操作符類別語義示例
Where篩選操作符(Restriction)Where(Predicate)Dim lowNums = numbers.Where(Function(num) num < 5)
Select投影操作符(Projection)Select(匿名函數)Dim lowNums = numbers.Select(Function(num) num*num)
SelectMany投影操作符(Projection)返回多行結果,用於多表的交叉連接(cross join)Dim res = Employees.SelectMany(Function(e) e.Family.Select(Function(c)c.name))
Skip分塊操作符(Partitioning)跳過前n個元素Dim res=data.Skip(4)
SkipWhile分塊操作符(Partitioning)跳過起始處使條件為真的所有元素Dim res=data.SkipWhile(Function(i) i%2 = 0)
Take分塊操作符(Partitioning)返回開頭之處的n個元素Dim res=data.Take(3)
TakeWhile分塊操作符(Partitioning)返回起始處使條件為真的所有元素Dim res=data.TakeWhile(Function(i) i%3 = 0)
Join連接操作符內連接兩個或多個表,僅限於Equals運算符from sup in suppliers join cust in customers on sup.Country equals cust.Country
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)
Concate合併操作符用於連接兩個序列returnValue = firstSeq.Concat(secondSeq)
OrderBy排序操作符(Ordering)Dim query As IEnumerable(Of Person) = Persons.OrderBy(Function(p) p.Age)
OrderByDescending排序操作符(Ordering)
ThenBy排序操作符(Ordering)
ThenByDescending排序操作符(Ordering)
Reverse排序操作符(Ordering)
GroupBy分組操作符
Dim numbers = {5, 4, 1, 3, 9, 8, 6, 7, 2, 0}
Dim numberGroups = From num In numbers Group num By remainder5 = (num Mod 5) Into numGroup = Group Select New With {.Remainder = remainder5, .numbers = numGroup}
Distinct集合操作符去重複
Union集合操作符集合併
Intersect集合操作符集合交
Except集合操作符集合差
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();
Cast轉換操作符把序列的所有元素轉換為指定類型
SequenceEqual相等操作符兩個序列的元素依次相同返回真。使用元素所屬類的IEqualityComparer(Of T) 泛型界面做相等比較
First元素操作符返回序列第一個元素(或滿足條件第一個元素),沒有則異常
FirstOrDefault元素操作符返回序列第一個元素,沒有則返回空或默認值
Last元素操作符返回序列最後一個元素,沒有則異常
LastOrDefault元素操作符返回序列最後一個元素,沒有則返回空或默認值
Single元素操作符返回序列唯一元素,如果沒有元素或多個元素則異常
SingleOrDefault元素操作符返回序列唯一元素,如果沒有元素或多個元素則空或默認值
ElementAt元素操作符返回序列指定元素,失敗則異常
ElementAtOrDefault元素操作符返回序列指定元素,失敗則空或默認值
DefaultIfEmpty元素操作符返回序列,如果序列為空則返回元素的默認值
For Each number As Integer In numbers.DefaultIfEmpty()〈br> output.AppendLine(number)
Next
All序列元素數量操作符序列所有元素滿足條件則為真
Any序列元素數量操作符序列有一個元素滿足條件則為真
Contains序列元素數量操作符是否包含一個元素
Count統計操作符計數,可選一個謂詞int oddNumbers = numbers.Count(n => n% 2 == 1);
LongCount統計操作符計數,返回Int64類型
Sum統計操作符求和,可選對一個lambda函數表達式double totalChars = words.Sum(w => w.Length);
Min統計操作符最小值,可選對一個lambda函數表達式int shortestWord = words.Min(w => w.Length);
Max統計操作符
Average統計操作符
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。
● C#3.0
● F#1.1.8.1
● Visual Basic 2008(9.0)
註:C++/CLI尚未支持LINQ。但是有第三方的C++包,以及第三方的PHP包

示例


一個簡單例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
另一個例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// the Northwind type is a subclass of DataContext created by SQLMetal
// Northwind.Orders is of type Table
// Northwind.Customers is of type TableNorthwind 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, where T is the anonymous type generated by the compilerforeach (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);
}

支持


LINQ當前由Visual Studio2008、2010、2012、2013、2015、2017支持。

語言擴展


微軟同樣提供了LINQExtender,允許用戶在不了解LINQ實現細節的情況下,編寫自己的LINQ擴展。 如:LINQ to Twitter,LINQ to Oracle,LINQ to Active Directory等。

語法實例


C#3.0 LINQ 查詢語法
首先來看一個很簡單的LINQ查詢例子,查詢一個int數組中小於5的數字,並按照大小順序排列:
static void Main(string[] args)
{
int[] arr = new int[] { 8, 5, 89, 41, 1, 2, 3, 65, 1 };
var m = from n in arr where n < 5 orderby n descending select n;//小於5,並且倒序排列顯示
foreach (var n in m)
{
Console.WriteLine(n);
}
Console.ReadLine();
}
上述代碼除了LINQ查詢語法外,其他都是我們所熟悉的語法,而LINQ查詢語法跟SQL查詢語法很相似,除了先後順序。
Q:為何 LINQ 查詢語法是以 from 關鍵字開頭的,而不是以 select 關鍵字開頭的?select 開頭這種寫法跟SQL的寫法更接近,更易懂呀?
A:簡單來說,為了IDE的智能感知(Intellisense)這個功能,select 關鍵字放在後面了。
編程語言以 select 開頭寫LINQ查詢語法不是沒出現過,你如果使用過2005年的VB9 CTP 版本,那時候VB9的LINQ查詢語法就是 select 關鍵字在前面,但是 select 關鍵字在前面,在做智能感知(Intelisence)時候就很頭大。經過微軟IDE組的權衡,確定了把 from 關鍵字放在最前面。
那時候 VB9 LINQ的查詢語法還是 select 參數在最前面。不過後來 VB9 測試版改成了跟 C# 一樣的做法, from 關鍵字放在最前面了。
更詳細的解釋,來自裝配腦袋
假設你要書寫這樣的代碼:Select p. Name, p.Age From p In persons Where xxx ,代碼是一個個字元輸入的。
我們在寫到 p in persons 之前,p 的類型是無法推測的,所以寫 Select p. 的時候,Name之類的屬性不會彈出智能提示來。
這樣就需要先去寫 From 這句,再回來寫 Select。
微軟IDE組經過反覆考慮決定,還不如就把 Select 寫到後面了。於是編程語言中的寫法就確定這樣來寫了。
VB9 的這個變化可以參看這篇博客:
Select/From vs. From/Select revisited...
我們再來看一個稍稍複雜的LINQ查詢:
在我們列舉的語言字元串中,我們希望按照字元長短,分類列舉出來,實現代碼如下:
static void Main(string[] args)
{
string[] languages = { "Java", "C#", "C++", "Delphi", "VB.net", "VC.net", "C++Builder", "Kylix", "Perl", "Python" };
var query = from item in languages
group item by item.Length into lengthGroups
orderby lengthGroups.Key
select lengthGroups;
foreach (var item in query)
{
Console.WriteLine("strings of length{0}", item.Key);
foreach (var val in item)
{
Console.WriteLine(val);
}
}
Console.ReadLine();
}
其中的 into 關鍵字表示 將前一個查詢的結果視為後續查詢的生成器,這裡是跟 group by 一起使用的。
LINQ中的Group by不要跟 SQL 中的Group by 混淆,SQL由於是二維結構,Group by 的一些邏輯受二維結構的約束,無法像 LINQ 中的Group by 這麼靈活。
事實上,LINQ的查詢語法存在以下兩種形式:
查詢方法方式:(Methord Syntax)
主要利用System.Linq.Enumerable類中定義的擴展方法和Lambda表達式方式進行查詢
查詢語句方式:(Query Syntax)一種更接近SQL語法的查詢方式,可讀性更好。

原理淺析


LINQ(Language Integrated Query)是Visual Studio 2008中的領軍人物。藉助於LINQ技術,我們可以使用一種類似SQL的語法來查詢任何形式的數據。目前為止LINQ所支持的數據源有SQL Server、Oracle、XML(標準通用標記語言下的一個應用)以及內存中的數據集合。開發人員也可以使用其提供的擴展框架添加更多的數據源,例如MySQL、Amazon甚至是GoogleDesktop。
一般來講,這類查詢語句的一個重要特點就是可以并行化執行。雖然有些情況下并行可能會帶來一些問題,但這種情況非常少見。這樣也就水到渠成地引出了PLINQ這個并行處理的LINQ類庫。
PLINQ原名為Parallel LINQ,支持XML和內存中的數據集合。執行於遠程伺服器上的查詢語句(例如LINQ to SQL)顯然無法實現這個功能。
將LINQ語句轉換為PLINQ語句極為簡單——只需要在查詢語句中From子句所指定的數據源的最後添加.AsParallel()即可。隨後Where、OrderBy和Select子句將自動改為調用這個并行的LINQ版本。
據MSDN Magazine介紹,PLINQ可以以三種方式執行。第一種是管道處理:一個線程用來讀取數據源,而其他的線程則用來處理查詢語句,二者同步進行——雖然這個單一的消費線程可能並不那麼容易與多個生產線程同步。不過若是能夠仔細配置好負載平衡的話,仍然會極大地減少內存佔用。
第二種模式叫做“stop and go”,用於處理結果集需要被一次返回時(例如調用ToList、ToArray或對結果排序)的情況。在這種模式下,將依次完成各個處理過程,並將結果統一返回給消費線程。這個模式在性能上將優於第一種模式,因為它省去了用來保持線程同步所花費的開銷。
最後一種方法叫做“inverted enumeration”。該方法並不需要實現收集到所有的輸出,然後在單一的線程中處理,而是將最終調用的函數通過ForAll擴展傳遞到每個線程中。這是目前為止最快的一種處理模式,不過這需要傳遞到ForAll中的函數是線程安全的,且最好不包含任何lock之類的互斥語句。
若是PLINQ中任意的一個線程拋出異常,那麼所有的其他線程將會被終止。若是拋出了多個異常,那麼這些異常將被組合成一個MultipleFailuresException類型的異常,但每個異常的調用堆棧仍會被保留。

使用優點


1、無需複雜學習過程即可上手
2、編寫更少代碼即可創建完整應用。
3、更快開發錯誤更少的應用程序。
4、無需求助奇怪的編程技巧就可合併數據源。
5、能夠大幅減少過程式控制制語句的代碼塊,使代碼的可讀性和可維護性大幅提高。
6、任何對象或數據源都可以定製實現Linq適配器,為數據交互帶來真正方便。

函數支持


支持以下公共語言運行時 (CLR) 方法和屬性,因為它們可以在查詢表達式中進行轉換以包含在OData服務的請求 URI 中:
String成員支持的 OData 函數
Concatstring concat(string p0, string p1)
Containsbool substringof(string p0, string p1)
EndsWithbool endswith(string p0, string p1)
IndexOfint indexof(string p0, string p1)
Lengthint length(string p0)
Replacestring replace(string p0, string find, string replace)
Substringstring substring(string p0, int pos)
Substringstring substring(string p0, int pos, int length)
ToLowerstring tolower(string p0)
ToUpperstring toupper(string p0)
Trimstring trim(string p0)
DateTime成員1支持的 OData 函數
Dayint day(DateTime p0)
Hourint hour(DateTime p0)
Minuteint minute(DateTime p0)
Monthint month(DateTime p0)
Secondint second(DateTime p0)
Yearint year(DateTime p0)
1也支持Visual Basic中等效的Microsoft.VisualBasic.DateAndTime的日期和時間屬性以及DatePart方法。
Math成員支持的 OData 函數
Ceilingdecimal ceiling(decimal p0)
Ceilingdouble ceiling(double p0)
Floordecimal floor(decimal p0)
Floordouble floor(double p0)
Rounddecimal round(decimal p0)
Rounddouble round(double p0)
Expression成員支持的 OData 函數
TypeIsbool isof(type p0)
客戶端或許還可以在客戶端上計算其他 CLR 函數。對於無法在客戶端上計算以及無法轉換為有效請求URI以便在伺服器上計算的任何錶達式,將引發 NotSupportedException。