linq查詢創建策略及數據轉換--學習linq的資料和筆記(六)

創建複雜linq查詢的創建策略其實跟子查詢有些相似,就是在查詢的過程中對數據進行進一步的處理,資料中介紹了三種創建策略如下(以下內容來自博客園)

 漸進式創建查詢

漸進式創建查詢就是通過鏈接查詢運算符的方式來創建LINQ查詢。因爲每一個查詢運算符返回一個裝飾者sequence,所以我們可以在其之上繼續調用其它查詢運算符。使用這種方式有如下幾個優點:

  • 使得查詢易於編寫
  • 我們可以根據條件來決定是否調用某個查詢運算符,如:if (includeFilter) query = query.Where(…)

漸進的方式通常是對查詢的創建有益的,考慮如下的例子:我們需要在名字列表中去除所有名字的元音字母,然後對長度大於2的名字進行排序。在方法語法中,我們可以在一個表達式中完成這個查詢:

            string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
IEnumerable<string> query = names
.Select(n => n.Replace("a", "").Replace("e", "").Replace("i", "")
.Replace("o", "").Replace("u", ""))
.Where(n => n.Length > 2)
.OrderBy(n => n); // Result: Dck, Hrry, Mry

如果直接將上面的query改寫成查詢表達式語法,我們將會遇到麻煩,這時因爲查詢表達式要求要以Select或Group結束。但在上面的查詢中,我們需要先做Select(結果投影)去除元音字母,再做過濾和排序。如果把Select直接放到後面,那麼結果將會被改變。幸運的是,我們還是有辦法讓查詢表達式來完成上面的工作,得到我們期望的結果。 第一種方式就是查詢表達式的漸進式(分步)查詢:

複製代碼
            IEnumerable<string> query =
from n in names
select n.Replace("a", "").Replace("e", "").Replace("i", "")
.Replace("o", "").Replace("u", "");

query = from n in query
where n.Length > 2
orderby n
select n; // Result: Dck, Hrry, Mry
複製代碼

into關鍵字

在我們前面查詢表達式的例子中,select關鍵字的出現也就意味着查詢的結束了。而into關鍵字讓我們在結果投影之後還可以繼續我們的查詢,它是對分步構建查詢表達式的一種簡寫方式。現在我們可以使用into關鍵字來重寫上例中的查詢:

複製代碼
            IEnumerable<string> query =
from n in names
select n.Replace("a", "").Replace("e", "").Replace("i", "")
.Replace("o", "").Replace("u", "")
into noVowel
where noVowel.Length > 2
orderby noVowel
select noVowel; // Result: Dck, Hrry, Mry
複製代碼

我們只能在select和group子句後面使用into關鍵字,它會重新開始一個查詢,讓我們可以繼續引入where, orderby和select子句。儘管表面上看,我們重新創建了一個新的查詢,但當上面的查詢被翻譯成方法語法時,它只是一個查詢,一個鏈接了多個運算符的查詢,所以上面的寫法不會造成性能問題。

需要注意的是,所有的查詢變量在into關鍵字之後都不再可見,下面的例子就說明了這一點:

            var query =
from n1 in names
select n1.ToUpper()
into n2 //into之後只有n2可見
where n1.Contains("x") //Error: n1不可見
select n2;

要理解其原因,我們只要看看它編譯器爲它翻譯成對應的方法語法就能知曉:

            var query = names
.Select(n1 => n1.ToUpper())
.Where(n2 => n1.Contains("x")); //Error: n1不再可見,lambda表達式中只有n2

包裝查詢

漸進式查詢創建方式可以通過在一個查詢中嵌入另一個查詢來改寫,這樣可以把多個查詢組合成單個查詢,即:

var tempQuery = tempQueryExpr

var finalQuery = from … in (tempQuery)

可以改寫爲:

var query = from … in (tempQueryExpr)

上面兩種方式以及into關鍵字的工作方式是一樣的,編譯器都會把他們翻譯成一個鏈接查詢運算符。請看下面的示例:

複製代碼

string[] names = {"Tom","Dick","Harry","Mary","Jay" };

// 漸進式查詢(Progressive query building)
IEnumerable<string> query =
from nin names
select Regex.Replace(n,"[aeiou]","");

query = from n in query where n.Length > 2 orderby n select n;

// 用包裝查詢方式進行改寫(Wrapping Queries)
IEnumerable<string> query2 =
from n1in
(
from n2in names
select Regex.Replace(n2,"[aeiou]","")
)
where n1.Length >2
orderby n1
select n1;

// 與上面等價的方法語法
IEnumerable<string> query3 = names
.Select(n => Regex.Replace(n, "[aeiou]",""))
.Where(n => n.Length > 2)
.OrderBy(n => n);

 

 

LINQ中的數據轉換,也叫結果投影,是指LINQ查詢select的輸出。到目前爲止,我們還只是看到了輸出單個標量元素的示例。通過使用對象初始化器,我們可以輸出更爲複雜的結果類型。比如下面的示例,當我們在把姓名中的元音字母去掉之後,我還需要保存姓名的原始版本:

複製代碼
        class TempProjectionItem
{
public string Original;
public string Vowelless;
}

static void TestProjectionStrategy()
{
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };

IEnumerable<TempProjectionItem> temp =
from n in names
select new TempProjectionItem
{
Original = n,
Vowelless = Regex.Replace(n, "[aeiou]", "")
};
//我們可以繼續在結果中查詢
IEnumerable<string> query =
from item in temp
where item.Vowelless.Length > 2 //按去除元音字母版本過濾
select item.Original; //結果爲姓名原始版本
}
複製代碼

匿名類型

上面我們自己定義了類型TempProjectionItem來存放查詢的結果。通過使用匿名類型,我們可以省去這種中間類型的定義,而由編譯器來幫我們完成:

複製代碼
            var intermediate = from n in names
select new
{
Original = n,
Vowelless = Regex.Replace(n, "[aeiou]", "")
};
IEnumerable<string> query = from item in intermediate
where item.Vowelless.Length > 2
select item.Original;
複製代碼

需要注意的是,因爲匿名類型的確切類型名是由編譯器自動產生的,因此intermediate的類型爲:IEnumerable <random-compiler-produced-name> 。我們來聲明這種類型的唯一方式就是使用var關鍵字,這時,var不只是更加簡潔,而且也是必需的手段。

let關鍵字

let關鍵字讓我們可以在保持範圍變量的同時引入新的查詢變量。比如上面的示例,我們可以用let關鍵字作如下改寫:

            string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };
var query = from n in names
let Vowelless = Regex.Replace(n, "[aeiou]", "")
where Vowelless.Length > 2
select n; //正是因爲使用了let,此時n仍然可見

let關鍵字非常靈活和方便,就像例子看到的那樣。而且,我們可以使用多個let關鍵字,並且後面的 let表達式可以引用前一個let關鍵字引入的變量。

 

 

 

 

 

發佈了54 篇原創文章 · 獲贊 84 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章