c#初學-LINQ查詢表達式基礎


什么是查詢?它有什么用途?


“查詢”是指一組指令,這些指令描述要從一個或多個給定數據源檢索的數據以及返回的數據應該使用的格式和組織形式。查詢不同於它所產生的結果。

通常,源數據會在邏輯上組織為相同種類的元素序列。SQL 數據庫表包含一個行序列。與此類似,ADO.NETDataRow 包含一個 DataTable 對象序列。在 XML 文件中,有一個 XML 元素“序列”(不過這些元素按分層形式組織為樹結構)。內存中的集合包含一個對象序列。

從應用程序的角度來看,原始源數據的具體類型和結構並不重要。應用程序始終將源數據視為一個 IEnumerable(Of T)IQueryable(Of T) 集合。在 LINQ to XML 中,源數據顯示為一個 IEnumerable<XElement>。在 LINQ to DataSet 中,它是一個 IEnumerable<DataRow>。在 LINQ to SQL 中,它是您定義用來表示 SQL 表中數據的任何自定義對象的 IEnumerableIQueryable

指定此源序列后,查詢可以進行下列三項工作之一:

  • 檢索一個元素子集以產生一個新序列,但不修改單個元素。然后,查詢可以按各種方式對返回的序列進行排序或分組,如下面的示例所示(假定 scoresint[]):

    IEnumerable<int> highScoresQuery =
    from score in scores
    where score > 80
    orderby score descending
    select score;
  • 如上一個示例所述檢索一個元素序列,但是將這些元素轉換為具有新類型的對象。例如,查詢可以只從數據源中的某些客戶記錄檢索姓氏。或者,查詢可以檢索完整的記錄,再使用它構建另一個內存中對象類型甚至 XML 數據,然后生成最終的結果序列。下面的示例演示了從 intstring 的轉換。請注意 highScoresQuery 的新類型。

    IEnumerable<string> highScoresQuery2 =
    from score in scores
    where score > 80
    orderby score descending
    select String.Format("The score is {0}", score);
  • 檢索有關源數據的單一值,例如:

    • 符合某個條件的元素的數量。

    • 具有最大值或最小值的元素。

    • 符合某個條件的第一個元素,或一組指定元素中的特定值之和。例如,下面的查詢從 scores 整數數組中返回高於 80 的分數的數量。

    int highScoreCount =
    (from score in scores
    where score > 80
    select score)
    .Count();

    在上一個示例中,請注意在 Count 方法調用之前的查詢表達式兩旁使用了括號。另一種表示方式是使用一個新變量來存儲具體結果。此技術的可讀性更好,因為它將存儲查詢的變量與存儲結果的查詢區分開來。

    IEnumerable<int> highScoresQuery3 =
    from score in scores
    where score > 80
    select score;

    int scoreCount = highScoresQuery3.Count();

在上一個示例中,查詢是在 Count 調用中執行的,因為 Count 必須循環訪問結果以便確定 highScoresQuery 返回的元素數量。

“查詢表達式”是用查詢語法表示的查詢,是一流的語言構造。它就像任何其他表達式一樣,並且可以用在 C# 表達式有效的任何上下文中。查詢表達式由一組用類似於 SQL 或 XQuery 的聲明性語法編寫的子句組成。每個子句又包含一個或多個 C# 表達式,而這些表達式本身又可能是查詢表達式或包含查詢表達式。

查詢表達式必須以 from 子句開頭,並且必須以 selectgroup 子句結尾。在第一個 from 子句和最后一個 selectgroup 子句之間,查詢表達式可以包含一個或多個下列可選子句:whereorderbyjoinlet 甚至附加的 from 子句。還可以使用 into 關鍵字使 joingroup 子句的結果能夠充當同一查詢表達式中附加查詢子句的源。

查詢變量

在 LINQ 中,查詢變量是任何存儲查詢(而非查詢結果)的變量。更具體地說,查詢變量始終是一個可枚舉的類型,當在 foreach 語句中或在對其 IEnumerator.MoveNext 方法的直接調用中循環訪問它時,它會生成一序列元素。

下面的代碼示例演示了一個簡單的查詢表達式,它含有一個數據源、一個篩選子句和一個排序子句,但不對源元素進行轉換。select 子句結束了該查詢。

static void Main()
{
// Data source.
int[] scores = { 90, 71, 82, 93, 75, 82 };

// Query Expression.
IEnumerable<int> scoreQuery = //query variable
from score in scores //required
where score > 80 // optional
orderby score descending // optional
select score; //must end with select or group

// Execute the query to produce the results
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
}
// Outputs: 90 82 93 82

在上一個示例中,scoreQuery 是一個查詢變量,有時簡稱為“查詢”。查詢變量並不存儲實際的結果數據(這些數據是在 foreach 循環中產生的)。另外,當 foreach 語句執行時,查詢結果並不是通過查詢變量 scoreQuery 返回的。相反,它們是通過迭代變量 testScore 返回的。可以在另一個 foreach 循環中迭代 scoreQuery 變量。只要該變量和數據源都沒有修改,該變量都將產生相同的結果。

查詢變量可以存儲用查詢語法或方法語法(或二者的組合)表示的查詢。在下面的示例中,queryMajorCitiesqueryMajorCities2 都是查詢變量:

//Query syntax
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population > 100000
select city;


// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population > 100000);

另一方面,下面的兩個示例演示了不是查詢變量的變量,即使每個變量都用查詢進行了初始化。它們不是查詢變量的原因是它們存儲了結果:

int highestScore =
(from score in scores
select score)
.Max();

// or split the expression
IEnumerable<int> scoreQuery =
from score in scores
select score;

int highScore = scoreQuery.Max();

IEnumerable<City> largeCityList =
(from country in countries
from city in country.Cities
where city.Population > 10000
select city)
.ToList();

// or split the expression
IEnumerable<City> largeCityList2 =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;

List<City> largeCities = largeCityList2.ToList();
說明:

在 LINQ 文檔中,存儲查詢的變量在其名稱中包含單詞“query”,而存儲實際結果的變量在其名稱中不包含單詞“query”。

有關用於表示查詢的不同方式的更多信息,請參見查詢語法與方法語法 (LINQ)

查詢變量的顯式類型化和隱式類型化

本文檔通常提供查詢變量的顯式類型,以便演示查詢變量和 select 子句之間的類型關系。但是,也可以使用 var 關鍵字指示編譯器在編譯時推斷查詢變量(或任何其他本地變量)的類型。例如,還可以使用隱式類型化表示本主題前面部分中演示的查詢示例:

// Use of var is optional here and in all queries.
// queryCities is an IEnumerable<City> just as
// when it is explicitly typed.
var queryCities =
from city in cities
where city.Population > 100000
select city;

有關更多信息,請參見隱式類型的局部變量(C# 編程指南)查詢操作中的類型關系 (LINQ)

開始查詢表達式

查詢表達式必須以 from 子句開頭。它同時指定了數據源和范圍變量。在對源序列進行遍歷的過程中,范圍變量表示源序列中的每個后續元素。將根據數據源中元素的類型對范圍變量進行強類型化。在下面的示例中,因為 countriesCountry 對象數組,所以范圍變量也被類型化為 Country,這樣就可以使用點運算符來訪問該類型的任何可用成員。

IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;

在使用分號或延續子句退出查詢之前,范圍變量將一直位於范圍中。

查詢表達式可以包含多個 from 子句。當源序列中的每個元素本身就是集合或包含集合時,可使用附加的 from 子句。例如,假定您具有一個 Country 對象集合,而其中每個對象都包含一個名為 CitiesCity 對象集合。若要查詢每個 Country 中的 City 對象,請使用兩個from 子句,如下所示:

IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;

有關更多信息,請參見 from 子句(C# 參考)

結束查詢表達式

查詢表達式必須以 select 子句或 group 子句結尾。

group 子句

使用 group 子句可產生按照指定的鍵組織的組序列。鍵可以采用任何數據類型。例如,下面的查詢創建一個組序列,該序列包含一個或多個 Country 對象,並且它的鍵是字符串值。

var queryCountryGroups =
from country in countries
group country by country.Name[0];

有關分組的更多信息,請參見 group 子句(C# 參考)

select 子句

使用 select 子句可產生所有其他類型的序列。簡單的 select 子句只是產生與數據源中包含的對象具有相同類型的對象的序列。在此示例中,數據源包含 Country 對象。orderby 子句只是將元素重新排序,而 select 子句則產生重新排序的 Country 對象的序列。

IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
可以使用 select 子句將源數據轉換為新類型的序列。這一轉換也稱為“投影”。在下面的示例中, select 子句對一個匿名類型序列進行投影,該序列僅包含原始元素中各字段的子集。請注意,新對象是使用對象初始值設定項初始化的。
// Here var is required because the query
// produces an anonymous type.
var queryNameAndPop =
from country in countries
select new { Name = country.Name, Pop = country.Population };

有關使用 select 子句轉換源數據的所有方式的更多信息,請參見 select 子句(C# 參考)

使用“into”進行延續

可以在 selectgroup 子句中使用 into 關鍵字來創建用於存儲查詢的臨時標識符。當您必須在分組或選擇操作之后對查詢執行附加查詢操作時,需要這樣做。在下面的示例中,以一千萬人口范圍為界對 countries 進行分組。在創建這些組之后,使用附加子句篩選掉某些組,然后按升序對剩下的組進行排序。若要執行這些附加操作,需要使用由 countryGroup 表示的延續。

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
from country in countries
let percentile = (int) country.Population / 10000000
group country by percentile into countryGroup
where countryGroup.Key >= 20
orderby countryGroup.Key
select countryGroup;

// grouping is an IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
Console.WriteLine(grouping.Key);
foreach (var country in grouping)
Console.WriteLine(country.Name + ":" + country.Population);
}

有關更多信息,請參見 into(C# 參考)

篩選、排序和聯接

from 開始子句以及 selectgroup 結束子句之間,所有其他子句(wherejoinorderbyfromlet)都是可選的。任何可選子句都可以在查詢正文中使用零次或多次。

where 子句

使用 where 子句可以根據一個或多個謂詞表達式篩選掉源數據中的某些元素。以下示例中的 where 子句含有兩個謂詞。

IEnumerable<City> queryCityPop =
from city in cities
where city.Population < 200000 && city.Population > 100000
select city;

有關更多信息,請參見 where 子句(C# 參考)

orderby 子句

使用 orderby 子句可以按升序或降序對結果進行排序。您還可以指定次要排序順序。下面的示例使用 Area 屬性對 country 對象執行主要排序,然后使用 Population 屬性執行次要排序。

IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area > 500000, country.Population descending
select country;

ascending 關鍵字是可選的;如果未指定順序,則它是默認排序順序。有關更多信息,請參見 orderby 子句(C# 參考)

join 子句

使用 join 子句可以根據每個元素中指定鍵之間的相等比較,對一個數據源中的元素與另外一個數據源中的元素進行關聯和/或組合。在 LINQ 中,聯接操作是針對其元素具有不同類型的對象序列執行的。在聯接兩個序列之后,必須使用 selectgroup 語句指定要存儲到輸出序列中的元素。還可以使用匿名類型將每組關聯元素中的屬性組合為輸出序列的新類型。下面的示例對其 Category 屬性與 categories 字符串數組中的某個類別相匹配的 prod 對象進行關聯。其 Category 不與 categories 中的任何字符串匹配的產品會被篩選掉。select 語句投影了一個新類型,其屬性取自 catprod

var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new { Category = cat, Name = prod.Name };

通過使用 into 關鍵字將 join 操作的結果存儲到臨時變量中,還可以執行分組聯接。有關更多信息,請參見 join 子句(C# 參考)

let 子句

使用 let 子句可以將表達式(如方法調用)的結果存儲到新的范圍變量中。在下面的示例中,范圍變量 s 存儲了 Split 返回的字符串數組的第一個元素。

string[] names = { "Svetlana Omelchenko", "Claire O'Donnell", "Sven Mortensen", "Cesar Garcia" };
IEnumerable<string> queryFirstNames =
from name in names
let firstName = name.Split(new char[] { ' ' })[0]
select firstName;

foreach (string s in queryFirstNames)
Console.Write(s + " ");
//Output: Svetlana Claire Sven Cesar

有關更多信息,請參見 let 子句(C# 參考)

查詢表達式中的子查詢

查詢子句本身可能包含一個查詢表達式,該查詢表達式有時稱為“子查詢”。每個子查詢都以它自己的 from 子句開頭,該子句不一定指向第一個 from 子句中的同一數據源。例如,下面的查詢演示了一個在 select 語句中使用的查詢表達式,用來檢索分組操作的結果。

var queryGroupMax =
from student in students
group student by student.GradeLevel into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore =
(from student2 in studentGroup
select student2.Scores.Average())
.Max()
};

有關更多信息,請參見如何:對分組操作執行子查詢(C# 編程指南)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM