在關係型數據庫中,數據被組織放入規範化很好的表中,並通過簡單而又強大的語言SQL來進行訪問,SQL可以和數據庫中任何數據配合使用,因爲數據被放入表中,並遵從一些嚴格的規則。
然而在程序中卻與數據庫相反,保存在類對象或結構中的數據差異很大,因此,沒有通用的查詢語言來從數據結構中獲取數據,從對象獲取數據的方法一直都是作爲程序的一部分而設計的,然而使用LINQ可以輕鬆查詢對象集合。LINQ是.NET框架的擴展,它允許我們以使用SQL查詢數據庫的方式來查詢數據集合。使用LINQ可以從數據庫、程序對象的集合以及XML文檔中查詢數據。如下程序中語句中查詢的定義就是from和select關鍵字,儘管查詢在語句中定義,但直到最後的foreach語句請求其結果的時候纔會執行。
public class Program
{
static void Main(string[] args)
{
int[] numbers = { 2, 12, 5, 15 };
IEnumerable<int> lowNums = from n in numbers where n < 10 select n;
foreach (var x in lowNums)
Console.WriteLine(x);
}
}
匿名類型
匿名類型也叫無名類類型,創建匿名類型的變量使用相同的形式,但是沒有類名和構造函數,匿名類型經常用於LINQ查詢的結果之中。
(1)匿名類型只能和局部變量配合使用,不能用於類成員
(2)由於匿名類型沒有名字,我們必須使用var關鍵字作爲變量類型
(3)不能設置匿名類型對象的屬性,編譯器爲匿名類型創建的屬性是只讀的
當編譯器遇到匿名類型的對象初始化語句時,它創建了一個有名字的新類類型,對於每一個成員初始化語句,它推斷其類型並創建一個只讀屬性來訪問它的值,屬性和成員初始化語句具有相同的名字,匿名類型被構造後,編譯器創建了這個類型的對象,如果編譯器遇到了另一個具有相同的參數名、相同的推斷類型和相同順序的匿名類型,它會重用這個類型並直接創建新的實例,不會創建新的匿名類型。
LINQ查詢時的倆種語法(方法語法、查詢語法)
方法語法:使用標準的方法調用,這些方法是一組叫做標準查詢運算符的方法,方法語法是命令式的,它指明瞭查詢方法調用的順序,編譯器會將使用查詢語法表示的查詢,翻譯爲方法調用的形式,它和查詢語法在運行時沒有性能上的差異。
查詢語法:看上去和SQL語句很相似,使用查詢表達式形式書寫,查詢語法是聲明式的,也就是說,查詢描述的是你想返回的東西,但並沒有指明如何執行這個查詢。
public class Program
{
static void Main(string[] args)
{
int[] numbers = { 2, 5, 28, 31, 17, 16, 42 };
var numsQuery = from n in numbers where n < 20 select n; //查詢語法
var numsMethod = numbers.Where(x => x < 20); //方法語法
int numsCount = (from n in numbers where n < 20 select n).Count(); //倆種方法的組合
foreach (var x in numsQuery)
Console.WriteLine(x);
foreach(var x in numsMethod)
Console.WriteLine(x);
Console.WriteLine(numsCount);
}
}
查詢表達式的結構
(1)子句必須按照一定的順序出現
(2)from子句和select...group子句這倆部分是必需的
(3)在LINQ查詢表達式中,select子句在表達式最後,這與SQL的SELECT語句在查詢的開始處不一樣,C#這麼做的原因之一是讓vs智能感應能在我們輸入代碼時給我們更多選項。
from子句
from子句指定了要作爲數據源使用的數據集合,它還引入了迭代變量。
from子句與foreach語句的區別:
(1)foreach語句命令式地指定了要從第一個到最後一個按順序地訪問集合中的項,而from子句則聲明式地規定集合中的每個項都要被訪問,但沒有假定以什麼樣的順序。
(2)foreach語句在遇到代碼時就執行其主體,而from子句什麼也不執行,它創建可以執行查詢的後臺代碼對象,只有在程序的控制流遇到訪問查詢變量的語句時,纔會執行查詢。
join子句
LINQ中的join子句和SQL中的JOIN子句很相似,聯結操作接受倆個集合然後創建一個臨時的對象集合,每一個對象包含原始集合對象中的所有字段。
join子句示例:
查詢主體中的from...let...where
可選的from...let...where部分是查詢主體的第一部分,可以由任意數量的3個子句來組合(from、let、where)
from:
查詢表達式從from子句開始,後面跟查詢主體,主體本身可以從任何數量的其他from子句開始,沒個from子句都指定了一個額外的源數據集合並引入了要在之後運算的迭代變量。
let:
let子句接受一個表達式的運算符並且把它賦值給一個需要在其他運算中使用的標識符。
where:
where子句根據之後的運算來除去不符合指定條件的項。
orderby子句
orderby子句接受一個表達式並根據表達式按照順序放回結果項,order子句的默認排序方式是升序,可以使用ascending()升序和descending(降序)關鍵字顯示設置。
public class Program
{
static void Main(string[] args)
{
int[] numbers = { 2, 5, 28, 31, 17, 16, 42 };
var asc_qury = from n in numbers orderby n ascending select n; //升序
var desc_qury = from n in numbers orderby n descending select n; //降序
foreach (var v in asc_qury)
Console.WriteLine(v);
foreach (var v in desc_qury)
Console.WriteLine(v);
}
}
group by子句
group子句把select的對象根據一些標準進行分組,如果項包含在查詢的結果中,它們就可以根據某個字段的值進行分組,作爲分組的依據的屬性叫做鍵(key),group子句返回的不是原始數據源中項的枚舉,而是返回可以枚舉已經形成的項的分組的可枚舉類型,分組本身是可枚舉類型,它們可以枚舉實際的項。
static void Main(string[] args)
{
var students = new[]
{
new {LName="Jones",FName="Mary",Age=19,Major="History" },
new {LName="Smith",FName="Bob",Age=20,Major="CompSci" },
new {LName="Fleming",FName="Carol",Age=21,Major="History" },
};
var query = from student in students group student by student.Major;
foreach(var s in query)
{
Console.WriteLine("{0}", s.Key); //分組鍵
foreach (var t in s)
Console.WriteLine(" {0},{1}", t.LName, t.FName);
}
}
into子句
查詢延續子句可以接受查詢的一部分結果並賦予一個名字,從而可以在查詢的另一部分中使用。
static void Main(string[] args)
{
int[] _a = { 2, 3, 4, 5 };
int[] _b = { 4, 5, 6 };
var query = from a in _a
join b in _b on a equals b
into _c //查詢延續
from c in _c
select c;
foreach (var v in query)
Console.WriteLine(v); //4,5
}
標準查詢運算符
(1)被查詢的集合對象叫做序列,它必須實現IEnumerable<T>接口
(2)標準查詢運算符使用方法語法
(3)一些運算符返回IEnumerable對象,而其他的一些運算符返回標量,返回標量的運算符立即執行,並返回一個值,而不是可枚舉類型對象。
System.Linq.Enumerable類聲明瞭標準查詢運算符方法,然而這些方法不僅僅而是一些方法,它們是擴展了IEnumerable<T>泛型類的擴展方法,擴展方法如之前所介紹,擴展方法時公共的靜態方法,儘管定義在一個類中,但目的是爲另一個類(第一個形參)增加功能,該參數前必須有關鍵字this。
LINQ預定義的委託類型
很多標準運算符的參數中第一個參數是IEnumerable<T>對象的應用,之後的參數爲泛型委託,泛型委託用於給運算符提供用戶自定義的代碼,LINQ定義了倆套泛型委託類型與標準查詢運算符一起使用,即Func委託和Action委託,我們用作實參的委託對象必須是這些類型或這些形式之一,並且總是在類型參數列表中的最後一個。
使用: