LINQ&EF任我行(二)--LinQ to Object

(原創:灰灰蟲的家http://hi.baidu.com/grayworm
LinQ to Objects是LinQ家庭的核心,其它的LinQ也使用了與LinQ to Objects相同的查詢句法。最終編譯器都是把LinQ句法翻譯成擴展方法的鏈式表達式,同時把擴展方法中的lambda表達式轉換成匿名類中的匿名方法,然後再把查詢表達式編譯成MSIL。
LinQ to SQL、LinQ to DataSets、LinQ to Entities和LinQ to XML則不是把查詢表達式轉換成MSIL,而是把查詢表達式樹轉換成相應的特定查詢語言。LinQ to SQL會生成T-SQL,LinQ to Entities會生成eSQL,LinQ to XML會生成XPath語句等。

LinQ標準查詢操作符列表



《圖1》

在VS2008及以後的版本中提供了LinQ的查詢樣例程序。
\Programe Files\Microsoft Visual Studio 2008(10.0 VS2010)\Samples\1033文件夾下有個壓縮包CSharpSamples.zip,解壓它,繼續打開其中的文件夾
.\LinqSamples\SampleQueries\SampleQueries.sln,打開此解決方案,運行該項目,界面如下



《圖2》
在左邊選擇示例,右側上方會出現相應的LinQ代碼,右側下方會出現代碼的運行結果。通過這個樣例程序,我們可以學習LinQ的各種用法。

下面我們來學習LinQ常用操作符
一、篩選操作符Where
根據謂詞對源序列的內容進行篩選,類似於SQL中的where子句。

1.簡單where表達式
使用擴展方法
var query1 = CustomerList.Where(c => c.Country == “USA”);
使用查詢表達式語法
query1 = from c in CustomerList where c.Country == “USA” select c;
使用擴展方法需要向where方法中傳入lambda表達式。

2.複合where表達式
所謂的複合where表達式就是使用&&或||操作符對數據進行篩選
使用擴展方法
var query2 = CustomerList.Where(c => c.Country == “USA” &&   c.Orders.Any());
使用查詢表達式語法
query2 = from c in CustomerList where c.Country == “USA” &&   c.Orders.Any() select c;

Any()方法相當於Count()>0,如果集合中有元素就返回true。

3.使用Index參數和IndexOf()方法
index值代表的是集合中元素的索引號,在where()中使用index可以控制返回與指定索引號相關的集合數據。
泛是返回IEnumerable<T>類型的查詢操作都可以使用index。
使用擴展方法
var query3 = CustomerList.Where((c, index) => c.Country == “USA” &&   index   > 70);//返回國家是USA,索引號大於70的元素集合。
這裏lambda表達式中接收的參數是(c,index),索引號做爲第二個參數傳入。

也可以使用IndexOf()方法來實現上面的功能,如果使用IndexOf()的話,那lambda表達式中傳入的參數只需要一個就可以了。
使用擴展方法
var query3 = CustomerList.Where(c => c.Country == “USA” && CustomerList.IndexOf(c) > 70);
使用查詢表達式語法
query3 = from cust in CustomerList where cust.Country == “USA”&& CustomerList.IndexOf(cust) > 70 select cust;

二、投影運算符

投影運算符對應SQL中的“select 列名”子句

(一)Select
Select操作符是從序列源返回一組指定屬性
使用擴展方法
var infos = context.Infos.Where(p => p.Sex == true).Select(p => new { p.Name,p.Sex});
foreach (var c in infos)
{
    Console.WriteLine(c.Name + c.Sex);
}
使用查詢表達式語法
var infos = from p in context.Infos where p.Sex == true select new { p.Name, p.Sex };
foreach (var c in infos)
{
    Console.WriteLine(c.Name + c.Sex);
}
如果使用擴展方法則在Select()方法中使用lambda表達式p=>new {p.Name,p.Sex}來對列進行投影;如果使用查詢表達式語法的話,直接在select關鍵字後使用匿名類new { p.Name, p.Sex }即可。

(二)SelectMany
SelectMany操作符實際上實現的是相關數據的交叉連接操作。它根據lambda表達式從一對多的序列中返回指定的屬性。
比如:



《圖3》
查詢男員工的所有家庭成員:
使用擴展方法
var q = context.Infos.Where(p => p.Sex == true).SelectMany(f=>f.Families);
foreach (var n in q)
{
    Console.WriteLine(n.Name);
}
使用查詢表達式語法
var q = from p in context.Infos where p.Sex==true from f in p.Families select f;
foreach (var n in q)
{
    Console.WriteLine(n.Name);
}

三、分塊操作符
(一)Skip和Take
Skip是從序列中跳過元素的個數;Take是從序列中獲取元素的個數;
如:跳過集合的前2個元素,從第三個元素開始向後取4個元素。
使用擴展方法
var q = list.Skip(2).Take(4);
使用查詢表達式語法
var q = (from p in list select p).Skip(2).Take(4);

(二)SkipWhile和TakeWhile
SkipWhile:條件跳過,從序列第一個元素開始依次判斷,一直跳到不滿足條件的元素爲止,返回此元素及此元素之後的序列 ;
TakeWhile:條件抓取,從序列第一個元素開始依次判斷,只要滿足條件就進行下個元素判斷,直到不滿足條件的元素爲止,返回此元素之前的序列 ;
如:取集合中第一批“性別”是“男”的元素的集合。
使用擴展方法
var q = list.SkipWhile(p => p.Sex == false).TakeWhile(p => p.Sex == true);
使用查詢表達式語法
var q = (from p in list select p).SkipWhile(p => p.Sex == false).TakeWhile(p => p.Sex == true);

四、連接運算符
Join和GroupJoin操作符是把兩個相互獨立的對象通過關鍵屬性關聯起來。這種對象與對象的關聯與SQL中的Join關聯語法上有些不同。
1.LinQ的Join不支持SQL-92中的一些比較運算符,如>、<、<>等。它只支持相等運算符
2.在On子句中不能使用=來實現兩個對象之間的關聯,需要使用Equals運算符。
(一)Join

使用擴展方法
var list = infos.Join(works, p => p.Code, w => w.InfoCode, (p, w) => new { p.Name, w.Firm, w.Depart });
使用查詢表達式語法
var list = from p in infos join w in works on p.Code equals w.InfoCode select new { p.Name, w.Firm, w.Depart };
(二)GroupJoin
可以實現外聯效果

Join擴展方法與GroupJoin擴展方法簽名有些不一樣



《圖4》



《圖5》
在C#3.0查詢表達式語法中沒有GroupJoin語法,可以使用Join...into...來實現,它與Join不同的是,它可以實現類似於SQL外聯接的效果,而Join只實現類似於SQL內聯的效果。
使用查詢表達式語法
var list = from p in infos join w in works on p.Code equals w.InfoCode into bo from r in bo.DefaultIfEmpty() select new { p.Name, r};

五、連接運算符
Concat運算符用來把兩個序列連接到一個序列中,它類似於SQL中的關係或or運算符。
使用擴展方法

var q = infos.Select(p=>p.Name).Concat(works.Select(w=>w.Firm));
使用查詢表達式語法
var q = (from p in infos select p.Name).Concat(from w in works select w.Firm);

六、排序運算符
排序運算符一共包含五個運算符OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse
OrderBy:升序排序
OrderByDescending:降序排序
ThenBy:在OrderBy或OrderByDescending後實現多級排序中實現升序排序
ThenByDescending:在OrderBy或OrderByDescending後實現多級排序中實現降序排序
Reverse:順序倒轉

如:對所有人員先按照性別升序排序,再按照生日降序排序
使用擴展方法:
var q = infos.OrderBy(p => p.Sex).ThenByDescending(p => p.Birthday);
使用查詢表達式語法:
var q = from p in infos orderby p.Sex,p.Birthday descending select p;
在查詢表達式語法中實現多級排序類似於T-Sql中的方式。

七、分組操作符
分組操作符GroupBy用來按照元素的某個屬性來對序列中的元素進行分組。類似於SQL中的group by 子句,但它是對象的序列,還可以獲取每組中的每個元素對象。
如:按照性別對人員進行分組,並顯示每組中人員的信息
使用擴展方法:
var q = infos.GroupBy(p=>p.Sex);;
使用查詢表達式語法:
var q = from m in infos group m by m.Sex into g select g;
顯示分組數據:
foreach (var item in q)
{
    //item.Key代表分組後的關鍵字的值,在這裏是性別Sex的值
    Console.WriteLine("性別爲"+item.Key+"\t共有"+item.Count()+"人");
    foreach (var c in item)
    {
            Console.WriteLine("\t" + c.Name + "\t" + c.Sex);
    }
    Console.WriteLine("*********************");
}



《圖6》

八、集合操作符
集合操作符包括Distinct、Union、Intersect和Except,除了Distinct之外其餘的三個是用來把兩個集合拼合成一個集合。
(一)Distinct
Distinct操作符用來把序列中重複的值移除掉,類似於SQL中的Distinct
如:查看Infos集合中所有
使用擴展方法:
var q = infos.Select(p => p.Nation).Distinct();
使用查詢表達式語法:
var q = (from p in infos select p.Nation).Distinct();

(二)Union
Union操作符取兩個具有相同結構的集合並集,如果兩集合中有相同元素,則會自動濾去重複內容。而前面所講的Concat操作符只是將兩個集合進行合併,並不過濾重複元素。
如:兩個集合,其中temp是infos的子集。
var infos = from p in context.Infos.ToList() select p;
var temp = infos.Where(p => p.Sex == true);
使用擴展方法:
var q = infos.Union(temp);
使用查詢表達式語法:
var q = (from p in infos select p).Union(from m in temp select m);
運行結果中,子集的內容並沒有重複出現

(三)Intersect
Intersect操作符是取兩個具有相同結構的集合的交集部份。
如:兩個集合,其中temp是infos的子集。
var infos = from p in context.Infos.ToList() select p;
var temp = infos.Where(p => p.Sex == true);
使用擴展方法:
var q = infos.Intersect(temp);
使用查詢表達式語法:
var q = (from p in infos select p).Intersect(from m in temp select m);
運行結果中只顯示子集中的內容。

(四)Except
Except操作符是從一個集合中取另一個集合的差集,即從集合A中取出集合B中不包含的元素。
如:兩個集合,infos和temp,temp集合中包含了infos集合中不存在的元素
var infos = from p in context.Infos.ToList() select p;
var temp = infos.Where(p => p.Sex == true).ToList();
temp.Add(new Info
{
    Code = "p100",
    Name = "哈哈",
    Sex = false,
    Nation = "n004",
    Birthday = DateTime.Now
});
使用擴展方法:
var q = infos.Except(temp);
使用查詢表達式語法:
var q = (from p in infos select p).Except(from m in temp select m);

九、轉換操作符,用來改變集合的類型
(一)ToArray

把集合轉換爲數組形式,不延遲
使用擴展方法:

Info[] g = infos.ToArray();
使用查詢表達式語法:
Info[] g = (from p in infos select p).ToArray();
(二)ToList
把集合轉換爲泛型集合形式,不延遲
使用擴展方法:
List<Info> g = infos.ToList();
使用查詢表達式語法:
List<Info> g = (from p in infos select p).ToList();

(三)ToDictionary
把集合轉換成Dictionary<TKey,TElement>類型的集合,它每個元素的value值是原集合中的一個元素對象。
如:下面的代碼把集合的內容轉換爲一個字典集合,字典的key值是人員代號,字典的value值是info元素對象。

使用擴展方法:
var q = infos.ToDictionary(p=>p.Code);
使用查詢表達式語法:
var q = (from p in infos select p).ToDictionary(p=>p.Code);
調用語法
foreach (var item in q)
{
    Console.WriteLine(item.Key+item.Value.Name);
}
從運行結果中我們可以看出,字典的第二個參數就是原集合中的元素。

(四)ToLookup
把集合轉換成ILookup<TKey,TElement>類型的集合,ILookup<TKey,TElement>集合與Dictionary<TKey,TElement>集合不同的是:Dictionary<TKey,TElement>中Key和Value值一一對應,而ILookup<TKey,TElement>集合中Key和Value值是一對多的對應關係。
如:使用ILookup<TKey,TElement>把集合中的元素分組顯示
使用擴展方法:
var q = infos.ToLookup(p=>p.Nation);
使用查詢表達式語法:
var q = (from p in infos select p).ToLookup(p => p.Nation);
顯示數據的代碼:
foreach (var item in q)
{
    Console.WriteLine(item.Key);
    foreach (var single in item)
    {
        Console.WriteLine("\t" + single.Name);
    }
}
顯示效果:



《圖7》
(原創:灰灰蟲的家http://hi.baidu.com/grayworm

十、相等操作符:SequenceEqual
用來對兩個序列進行對比。如果所有元素的值相等,並且元素個數相等,並且元素的次序相等,那SequenceEqual操作符返回的是True,否則返回False
var s1 = infos.OrderBy(p => p.Code);
var s2 = infos.OrderByDescending(p=>p.Code);
var s3 = infos.OrderBy(p => p.Code);
Console.WriteLine(s1.SequenceEqual(s2)); //結果是False
Console.WriteLine(s1.SequenceEqual(s3)); //結果是True

十一、元素操作符:
元素操作符的作用是從IEnumerable<T>集合序列中返回一個指定的元素。
如果沒有找到指定的元素,所有的XXXDefault操作符返回空對象,並不會產生異常。而First、Last、Single和ElementAt操作符則會產生異常。

(一)First和FirstOrDefault
如果序列中包含一個或多個元素,這兩個操作符返回序列中的第一個元素。如果序列不包含任何元素,則FirstOrDefault操作符返回null值(引用類型)或默認值(值類型),而First操作符則產生異常信息。
如:查找第一個女生
使用擴展方法:
var item = infos.First(p=>p.Sex == false);
var item = infos.Where(p => p.Sex == false).First();
使用查詢表達式語法:
var item = (from p in infos where p.Sex == false select p).First();

(二)Last和LastOrdefault
如果序列中包含一個或多個元素,這兩個操作符返回序列中的最後一個元素。如果序列不包含任何元素,則LastOrDefault操作符返回null值(引用類型)或默認值(值類型),而Last操作符則產生異常信息。
如:查找最後一個女生
使用擴展方法:
var item = infos.Last(p=>p.Sex == false);
var item = infos.Where(p => p.Sex == false).Last();
使用查詢表達式語法:
var item = (from p in infos where p.Sex == false select p).Last();

(三)Single和SingleOrDefault
如果序列中有且只有一個元素,則這兩個操作符返回該元素
如果序列中沒有任何元素,則Single會產生異常,而SingleOrDefault則會返回null值(引用類型)或默認值(值類型)
如果序列中包含多個元素,則這兩個操作符都會產生異常。

(四)ElementAt和ElementAtOrDefault
這兩個操作符是根據索引號從序列中返回指定的元素,如果未找到元素ElementAt()會產生異常,而ElementAtOrDefault()則會返回默認實例。
如:取出集合中的第二個女生
使用擴展方法:

var item = infos.Where(p=>p.Sex == false).ElementAt(2);
使用查詢表達式語法:
var item = (from p in infos where p.Sex == false select p).ElementAt(2);

十二、元素數量操作符
判斷序列中元素是否滿足指定的條件返回bool型的值。帶有該操作符的話句不能實現延遲查詢,語句會被立即執行。
(一)Any
如果序列中存在任一個滿足條件的元素,就返回true
如:判斷是否存在代號爲P005的人員
使用擴展方法:
var q = infos.Any(p=>p.Code == "p005");
var q = infos.Where(p => p.Code == "p005").Any();
使用查詢表達式語法:
var q = (from p in infos where p.Code == "p005").Any();

(二)All
如果序列中所有元素都滿足條件,就返回true
如:判斷是否所有員工都是漢族
使用擴展方法:
var q = infos.All(p => p.Nation == "漢族");
使用查詢表達式語法:
var q = (from p in infos select p).All(p=>p.Nation == "漢族");

(三)Contains
判斷集合中是否包含指定的元素

十三、聚合操作符
聚合操作符類似於SQL中的聚合函數,所有帶有聚合操作符的LinQ語句都無延遲功能,會被立即被執行。

(一)Count

取得序列中滿足條件的元素的個數
使用擴展方法:

var q = infos.Count(p => p.Sex == false);
var q = infos.Where(p => p.Sex == false).Count();
使用查詢表達式語法:
var q = (from p in infos where p.Sex==false select p).Count();

(二)Min、Max、Sum和Average

分別是取得序列中所有元素中某屬性的最小值、最大值、總和、平均值等。
decimal? minFreightCharge = OrderList.Min(c => c.Freight);
decimal? maxFreightCharge = OrderList.Max(c => c.Freight);
decimal? sumFreightCharge = OrderList.Sum(c => c.Freight);
decimal? avgFreightCharge = OrderList.Average(c => c.Freight);

總結:
這一篇文章主要介紹了LinQ的標準查詢操作符(SQO)。對每個操作符都介紹了使用擴展方法的使用和查詢表達式的使用,對於這兩種用法大家應當都掌握住,尤其要記住擴展方法的使用。
由於時間的原因沒有對每個示例的運行結果抓圖顯示,朋友們可以自己測試一下結果。
上面的內容是LinQ to Object的基礎語法,也是其它LinQ的基礎,熟練使用這些操作符能夠使我們在LinQ天地中自由馳騁。

(原創:灰灰蟲的家http://hi.baidu.com/grayworm

 

(原創:灰灰蟲的家http://hi.baidu.com/grayworm

 

轉自:http://hi.baidu.com/grayworm/item/6b4407329ab81df2e7bb7aa1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章