一·前言
Linq 英文全稱Language Integrated Query,它提供了C#程式設計語言中的查詢文法,可以使用相同的文法訪問不同的資料來源。並且Linq還提供了不同資料來源的抽象層,所以可以使用相同的文法。本次筆記的主要內容如下:
● 用List<T>在對象上執行傳統查詢
● 擴充方法
● λ運算式
● LINQ 查詢
● 標準查詢操作符
● 運算式樹狀架構
● LINQ 提供者
一。用List<T>在對象上執行傳統查詢
首先我們來看一個最簡單的例子,請看下面的代碼:
List<string> strList = new List<string>();
strList.Add("AAA");
strList.Add("BABB");
strList.Add("ACCC");
strList.Add("BBB");
strList.Add("CCC");
var res = strList.FindAll(delegate(string s) { return s.Contains("A"); });
res.Sort(delegate(string a, string b) { return a.CompareTo(b); });
this.GridView1.DataSource = res;
this.GridView1.DataBind();
這段代碼是從strList中尋找所有包含字母A的記錄,並且將得到的結果進行進行從小到大的排序(如果將return a.CompareTo(b); 換成return b.CompareTo(a);結果則是從大到小的順序進行排列了)。相信很多人用過這樣的方式來對結果集進行過濾和排序操作。但是如果你想在任何集合都可以使用這兩個方法,那你就可能會使用到擴充方法了。擴充方法是C#3.0的新增特性,這也是上述例子邁向LINQ 的第一個變化。下面我們就來介紹一下擴充方法。
二。擴充方法
請看下面關於擴充方法的例子
public static class helper
{
public static string MD5Hash(this string s)
{
return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(s, "MD5");
}
public static bool In(this object o, IEnumerable b)
{
foreach (object obj in b)
{
if (obj == o)
return true;
}
return false;
}
}
// 調用擴充方法
Response.Write("123456".MD5Hash());
Response.Write("1".In(new[]{"1","2","3"}));
上面的例子是對string進行了擴充,這樣的話只要在當前命名空間下任何string對象都可以利用該擴充方法。這樣會給代碼帶來很大的方便。
既然有了擴充方法,我們的Linq自然就很容易就出現了。就如同下面的Linq例子一樣
MethodInfo[] methods = typeof(string).GetMethods();
var result = from m in methods
where m.IsStatic != true
select m.Name;
foreach (var r in result)
{
Response.Write(r.ToString()+"<br/>");
}
這就是Linq最典型的樣子。是不是比較像我們平時用的SQL呢?呵呵雖然還有不小的差別,但是Linq的引入實在是C#的一種革命性的變化。他不僅好用,更給枯燥的C#代碼注入了一股鮮活的思想。
三。λ運算式
C# 3.0 給匿名方法提供了一個新的文法——λ運算式(也叫做Lambda運算式)。除了把匿名方法傳送給Where()、OrderbyDescen
ding()和Select()方法之外,還可以使用λ運算式。
λ 運算式參見第7 章。λ運算式在LINQ 中非常重要,所以下面複習一下該文法。詳細資料可參見第7
章。
比較λ運算式和匿名委託,會發現許多類似之處。λ運算子=>的左邊是參數,不需要添加參數類型,因
為它們是由編譯器解析的。λ運算子的右邊定義了執行代碼。在匿名方法中,需要花括弧和return 語句。在
λ運算式中,不需要這些文法元素,因為它們是由編譯器處理的。如果λ運算子右邊有多個語句,也可以使
用花括弧和return 語句。
例如下面的例子
var list = new[] { "aa", "bb", "ca" };
var resultList = Array.FindAll(list, s => (s.IndexOf("a") > -1));
foreach (var v in resultList)
Response.Write(v+"<br/>");
s就像是我們在第一個例子中使用的delegate(string s)裡面的s一樣只是這種使用方法更加的靈活和方便。
四。LINQ 查詢
我們就以NorthWind資料庫為例,添加一個Linq to Sql Classes命名為Customer.dbml,然後開啟Server Explorer設定好串連參數,選擇Customers表直接拖拽到Customer.dbml,開啟Customer.design.cs檔案我們可以看到Visual Studio為我們產生了很多代碼。這些代碼我們會在後面的內容裡詳細的講解。現在主要來講一下Linq查詢以及後面的內容。
LINQ 查詢是C#語言中的一個簡化查詢記號。編譯器編譯查詢運算式,調用擴充方法。查詢運算式只是C#中的一個文法,但不需要修改底層的IL 代碼。查詢運算式必須以from子句開頭,以select或group子句結束。在這兩個子句之間,可以使用where、orderby、join、let 和其他from子句。注意,變數query只指定了LINQ查詢。該查詢不是通過這個指派陳述式執行的,只要使用foreach逐一查看查詢,該查詢就會執行。
延遲查詢的執行
在運行期間定義查詢運算式時,查詢就不會運行。查詢會在迭代資料項目時運行。再看看擴充方法Where()。它使用yield return語句返回謂詞為true的元素。因為使用了yield return語句,所以編譯器會建立一個列舉程式,在訪問枚舉中的項後,就返回它們。
public static IEnumerable<T> Where<T>(this IEnumerable<T> source,Func<T, bool> predicate)
{
foreach (T item in source)
if (predicate(item))
yield return item;
}
請看一下下面的代碼
var names = new List<string>{"Nino","Alberto", "Juan", "Mike", "Phil"};
var namesWirhJ = (from name in names where name.StartsWith("J") orderby name select name);
Response.Write("First Iteration<br/>");
foreach (var name in namesWirhJ)
Response.Write(name + "<br/>");
names.Add("John");
names.Add("Jim");
names.Add("Jack");
names.Add("Denny");
Response.Write("Second iteration<br/>");
foreach (var name in namesWirhJ)
Response.Write(name + "<br/>");
其返回結果為
First iteration
Juan
Second iteration
Jack
Jim
John
Juan
但是當我們修改一下代碼將 var namesWirhJ = (from name in names where name.StartsWith("J") orderby name select name);換做var namesWirhJ = (from name in names where name.StartsWith("J") orderby name select name).ToList();那麼結果就成了
First iteration
Juan
Second iteration
Juan
這就是我們需要注意的地方了,每次在迭代中使用查詢時,都會調用擴充方法。在大多數情況下,這是非常有效,因為我們可以檢測出來源資料中的變化。但是在一些情況下,這是不可行的。調用擴充方法ToArray()、ToEnu
merable()、ToList()等可以改變這個操作。這也就是為什麼我們需要示範ToList()方法了。ToList 迭代集合,返回一個實現了List<string>的集合。之後對返回的列表迭代兩次,在這個過程中,資料來源被修改了,但是原來的集合并沒有改變。這樣可以用來實現一些比較複雜的商務邏輯。
五。標準查詢操作符
Where、OrderByDescending 和Select 只是LINQ 的幾個查詢操作符。LINQ 查詢為最常用的操作符定
義了一個聲明文法。還有許多標準查詢操作符。下表列出了Linq的標準查詢操作符。
Where OfType<TResult> |
過濾操作符定義了返回元素的條件。在Where 查詢操作符中,可以使用謂詞, 例如λ運算式定義的謂詞,來返回布爾值。OfType<TResult>根據類型過濾元素,只 返回TResult 類型的元素 |
Select和SelectMany |
投射操作符用於把對象轉換為另一個類型的對象。Select 和SelectMany 定義 了根據選取器函數選擇結果值的投射 |
OrderBy,ThenBy OrderByDescending ThenByDescending Reverse |
排序操作符改變所返回的元素的順序。OrderBy 按升序排序, OrderByDescending 按降序排序。如果第一次排序的結果很類似,就可以使用 ThenBy 和ThenBy Descending 操作符進行第二次排序。Reverse 反轉集合中元 素的順序 |
Join,GroupJoin |
串連運算子用於合并不直接相關的集合。使用Join 操作符,可以根據鍵選擇 器函數串連兩個集合,這類似於SQL 中的JOIN。GroupJoin 操作符串連兩個集 合,組合其結果 |
GroupBy |
組合運算子把資料放在組中。GroupBy 操作符組合有公用鍵的元素 |
Any,All,Contains |
如果元素序列滿足指定的條件,量詞操作符就返回布爾值。Any,All 和Contains 都是量詞操作符。Any 確定集合中是否有滿足謂詞函數的元素;All 確定集合 中的所有元素是否都滿足謂詞函數;Contains 檢查某個元素是否在集合中。這些操作 符都返回一個布爾值 |
Take,Skip, TakeWhile SkipWhile |
分區操作符返回集合的一個子集。Take、Skip、TakeWhile 和SkipWhile 都是 分區操作符。使用它們可以得到部分結果。使用Take 必須指定要從集合中提 取的元素個數;Skip 跳過指定的元素個數,提取其他元素,TakeWhile 提取條件為真的 元素 |
Distinct,Union Intersect,Except |
Set 操作符返回一個集合。Distinct 從集合中重複資料刪除的元素。除了Distinct 之外,其他Set 操作符都需要兩個集合。Union 返回出現在其中一個集合中的 元素。Intersect 返回兩個集合中都有的元素。Except 返回只出現在一個集合 中的元素 |
First FirstOrDefault Last LastOrDefault ElementAt ElementAtOrDefault Single SingleOrDefault |
這些元素操作符僅返回一個元素。First 返回第一個滿足條件的元素。 FirstOrDefault 類似於First,但如果沒有找到滿足條件的元素,就傳回型別 的預設值。Last 返回最後一個滿足條件的元素。ElementAt 指定了要返回的元 素的位置。Single 只返回一個滿足條件的元素。如果有多個元素都滿足條件, 就拋出一個異常 |
Count,Sum,Min, Max,Average, Aggregate |
合計操作符計算集合的一個值。利用這些合計操作符,可以計算所有值的總和、 元素的個數、值最大和最小的元素,平均值等 |
ToArray ToEnumerable ToList ToDictionary toType<T> |
這些轉換操作符將集合轉換為數組、IEnumerable、IList、IDictionary 等 |
Empty,Range, Repeat |
這些產生操作符返回一個新集合。使用Empty,集合是空的,Range 返回一系列數 字,Repeat 返回一個始終重複一個值的集合 |
註:本文內容摘自http://blog.sina.com.cn/s/blog_5df2629a0100lr3u.html
ASP.NET開發技術交流群: 67511751(人員招募中...)