前言
最早使用到Lambda表達式是因為一個需求:
如果一個數組是:int[] s = new int[]{1,3,5,9,14,16,22};
例如只想要這個數組中小於15的元素然后重新組裝成一個數組或者直接讓s返回一個新數組該怎么截取?
最開始的想法就是將這個s遍歷一遍然后判斷下再來重新組裝成新的數組.好麻煩是不是? 於是便百度到了一個叫做Lambda的東西, 所以用了之后效果如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] s = new int []{ 1,3,5,9,14,16,22 }; 6 var result = from n in s where n < 15 select n; 7 int[] b = result.ToArray(); 8 for (int i = 0; i < b.Length; i++) 9 { 10 Console.WriteLine(b[i]); 11 } 12 Console.ReadKey(); 13 } 14 }
打印結果如我們所想: 1, 3, 5, 9, 14.
剩下的就是在真實的項目中接觸到的, 在這里只是作為舉例, 不做細致講解:
1 var splitTexts = cmbValidationText.Split(new string[] { IncidentConstant.Comma },
StringSplitOptions.RemoveEmptyEntries); 2 if (cmbValidation.Items != null && cmbValidation.Items.Count > 0) 3 { 4 foreach (var splitText in splitTexts) 5 { 6 bool valid = cmbValidation.Items.Any(item => (item != null) && (item.Enabled) &&
(string.Equals(splitText, item.Prefix, StringComparison.OrdinalIgnoreCase))); 7 8 if (!valid) 9 { 10 invalidText += splitText.ToString() + CommaAndBlank; 11 isInvalidTextExist = true; 12 } 13 } 14 } 15 16 var categoryAndCapabilities = capabilities.Select(item => 17 { 18 PRResponseCategory category = null; 19 PRCapability prCapability = provisioningManager.GetPRCapabilityByKey(item.PRCapabilityKey.
GetValueOrDefault()); 20 if (prCapability != null) 21 { 22 category = statusMonitorDao.GetResponseCategoryByKey(prCapability.ResponseCategoryKey.
GetValueOrDefault()); 23 } 24 return new { Category = category, PRCapability = prCapability, Capability = item }; 25 }) 26 .Where(item => (item != null && item.Category != null && item.PRCapability != null)) 27 .OrderBy(item => item.Category.Code) 28 .ThenBy(item => item.PRCapability.AllowNumeric.GetValueOrDefault() ? 1 : 0) 29 .ThenBy(item => item.PRCapability.CapabilityCode) 30 .GroupBy(item => item.PRCapability.ResponseCategoryKey.GetValueOrDefault()) 31 .ToDictionary(grouping => grouping.Key, grouping => grouping.ToList());
這里會不會覺得很神奇? 那么下面就開始Lambda及Linq之旅吧.
1,Linq解析
Linq是Language Integrated Query的縮寫, 即"語言集成查詢"的意思. 它主要包含4個組件: Linq to Object, Linq to XML, Linq to DataSet 和Linq to Sql.
更多詳細內容可以查看一個國外網站: http://www.dotnetperls.com/linq
下面步入正題:
(1),查詢表達式
查詢表達式是一種使用查詢語法表示的表達式,它用於查詢和轉換來自任意支持LINQ的數據源中的數據。查詢表達式使用許多常見的C#語言構造,易讀簡潔,容易掌握。它由一組類似於SQL或XQuery的聲明性語法編寫的子句組成。每一個子句可以包含一個或多個C#表達式。這些C#表達式本身也可能是查詢表達式或包含查詢表達式。
查詢表達式必須以from子句開頭,以select或group子句結束。第一個from子句和最后一個select子句或group子句之間,可以包含一個活多個where子句、let子句、join子 句、orderby子句和group子句,甚至還可以是from子句。它包括8個基本子句,具體說明如下所示。
●from子句:指定查詢操作的數據源和范圍變量。
●select子句:指定查詢結果的類型和表現形式。
●where子句:指定篩選元素的邏輯條件。
●let子句:引入用來臨時保存查詢表達式中的字表達式結果的范圍變量。
●orderby子句:對查詢結果進行排序操作,包括升序和降序。
●group子句:對查詢結果進行分組。
●into子句:提供一個臨時標識符。join子句、group子句或select子句可以通過該標識符引用查詢操作中的中堅結果。
●join子句:連接多個用於查詢操作的數據源。
1.1,select,from, where子句:
示例1
下面創建一個查詢表達式query,該查詢表達式查詢arr數組中的每一個元素。
int[]arr =new int[]{0,1,2,3,4,5,6,7,8,9};
分析1

1 class Program 2 { 3 static void Main() 4 { 5 int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 6 var query = from n in arr 7 select n; 8 foreach (var element in query) 9 Console.WriteLine(element); 10 Console.ReadKey(); 11 } 12 }
示例2
下面創建一個查詢表達式query2.該查詢表達式查詢arr數組中大於6的元素。

1 class Program 2 { 3 static void Main() 4 { 5 int[] arr = new int[]{0,1,2,3,4,5,6,7,8,9}; 6 var query = from n in arr 7 where n >6 8 select n; 9 foreach (var element in query) 10 Console.WriteLine(element); 11 Console.ReadKey(); 12 } 13 }
分析2
變量只是保存查詢操作,而不是查詢的結果。當查詢表達式執行查詢操作時,才會計算該查詢表達式的結果。以上兩個變量的類型都屬於集合類型。
示例3
下面創建一個查詢表達式query。該查詢表達式包含兩個from子句,他們分別查詢兩個獨立的數據源;arr1數組和arr2數組。最后,使用select子句計算當前元素的和。

1 class Program 2 { 3 static void Main() 4 { 5 int[] arr1= new int[] {0,1,2,3,4,5,6,7,8,9}; 6 int[] arr2=new int[] {0,1,2,3,4,5,6,7,8,9}; 7 var query = from a in arr1 8 from b in arr2 9 select a +b; 10 11 foreach (var element in query) 12 Console.WriteLine(element); 13 Console.ReadKey(); 14 } 15 }
分析3
包含符合from子句的查詢表達式
在查詢表達式中,有可能查詢表達式的數據源中的每一個元素本身也作為該查詢表達式的數據源。那么要查詢數據源中的每一個元素中的元素,則需要使用符合from子句。符合from子句類似於嵌套的foreach語句。
1.2,let子句
let子句用來創建一個新的范圍變量,它用於存儲子表達式的結果。let子句使用編程者提供的表達式的結果初始化該變量。一旦初始化了該范圍變量的值,它就不能用於存儲其他的值。
示例
下面創建一個查詢表達式query。該查詢表達式從arr數組中查詢為偶數的元素。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] arr = new int[] {0,1,2,3,4,5,6,7,8,9}; 6 var query = from n in arr 7 let isEven = (n % 2 == 0 ? true : false) 8 where isEven 9 select n; 10 11 foreach (var element in query) 12 Console.WriteLine(element); 13 Console.ReadKey(); 14 } 15 }
分析
"return n%2==0?true:false"表達式判斷n元素是否為偶數。如果是,則返回true,否則返回false。“let isEven =return n%2==0?true:false”表達式使用let子句創建新的范圍變量isEven,用來保存"return n%2==0?true:false"表達式的結果。"where isEven"表達式使用where子句篩選isEven的值為true的元素。
1.3,orderby子句
orderby子句可使返回的查詢結果按升序或者降序排序。升序由關鍵字ascending指定,而降序由關鍵字descending指定。
注意:orderby子句默認排序方式為升序。
示例
下面創建一個查詢表達式query。該查詢表達式從arr數組中查詢大於1且小於6的元素,並且按照n元素對查詢結果進行降序排序。

1 class Program 2 { 3 static void Main() 4 { 5 int[] arr = new int[]{0,1,2,3,4,5,6,7,8,9}; 6 var query = from n in arr 7 where n>1 && n<6 8 orderby n descending 9 select n ; 10 foreach (var element in query) 11 Console.WriteLine(element); 12 Console.ReadKey(); 13 } 14 }
分析
orderby子句可以包含一個或多個排序表達式,各個排序表達式使用逗號(,)分隔。
1.4, group子句
group子句用來將查詢結果分組,並返回一對象序列。這些對象包含零個或更多個與改組的key值匹配的項,還可以使用group子句結束查詢表達式。
注意:每一個分組都不是單個元素,而是一個序列(也屬於集合)。
示例
下面創建一個查詢表達式query。該查詢表達式從arr數組中查詢大於1且小於6的元素,並且按照n%2表達式的值對查詢結果進行分組。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 6 var query = from n in arr 7 where n > 1 && n < 6 8 group n by n % 2; 9 10 foreach (var element in query) 11 Console.WriteLine(element); 12 Console.ReadKey(); 13 } 14 }
分析
query查詢表達式的結果是一個序列(類型為IEnumerable<IGrouping<int,int>>),該序列的元素類型為IGrouping<int,int>.其實,該查詢結果中的元素也是一個序列。
1.5, into子句
下面創建一個查詢表達式query。該查詢表達式從arr數組中查詢大於1且小於6的元素,並且按照n%2表達式的值對查詢結果進行分組。該查詢表達式的具體說明如下所示:
where n>1 && n<6:指定篩選大於1且小於6的元素。
group n by n%2 into g: 按照n%2表達式的值對查詢結果進行分組(0和0一組, 1和1 一組),並使用into子句創建臨時標識符g。該臨時標識符臨時保存分組結果。
from sn in g:從g標識符指定的每一個分組中查詢sn元素。
select sn:表示查詢sn元素。

1 class Program 2 { 3 static void Main() 4 { 5 int[] arr = new int[]{0,1,2,3,4,5,6,7,8,9}; 6 var query = from n in arr 7 where n>1&& n<6 8 group n by n%2 into g 9 from sn in g 10 select sn; 11 foreach (var element in query) 12 Console.WriteLine(element); 13 Console.ReadKey(); 14 } 15 }
分析
上述查詢表達式的查詢結果包括4個元素,依次為2、4、3和5
1.6, join子句
oin子句用來連接兩個數據源,即設置兩個數據源之間的關系。join子句支持以下3種常見聯接方式。
內部聯接:元素的鏈接關系 必須同時滿足兩個數據源,類似於SQL語句中的inner join子句。
分組聯接:包含into子句的join子句。
左外部聯接:元素的鏈接關系必須滿足聯接中的左數據源,類似於SQL語句中的left join子句。
內部聯接:join子句的內部聯接要求兩個數據源都必須存在相同的值,即兩個數據源都必須存在滿足聯接關系的元素。
示例
下面創建一個查詢表達式query。該查詢表達式使用join子句聯接了arra和arrb數組,具體說明如下。
創建arra數組,它包含10個元素(0~9)。
創建arrb數組,它包含5個元素(0、2、4、6和8)。
創建query查詢。
from a in arra:從arra數組中選擇元素,並表示為a。
where a < 7: 從arra數組中選擇小於7的元素
join b in arrb on a equals b: 將arra和arrb數組進行聯接,同時滿足a和b相等的條件。其中,b元素是arrb數組中的元素。
select a: 選擇a元素。

1 class Program 2 { 3 static void Main() 4 { 5 int[] arra = new int[] {0,1,2,3,4,5,6,7,8,9}; 6 int[] arrb = new int[]{0,2,4,6,8}; 7 var query = from a in arra 8 where a <7 9 join b in arrb on a equals b 10 select a; 11 foreach (var element in query) 12 Console.WriteLine(element); 13 Console.ReadKey(); 14 } 15 }
分析
上述查詢表達式首先選擇小於7的元素,(包括0~6),然后再喝arrb數組進行聯接,並獲取既包含在{0,1,2,3,4,5,6}集合中,又包含在arrb數組中的元素。最終,查詢表達式的結果包含4個元素(0、2、4和6)
分組聯接:join子句的分組聯接包含into子句的join子句的鏈接。它將左數據源與右數據源的元素一次匹配。左數據源的所有元素都出現在查詢結果中。若在右數據源中找到匹配項,則使用匹配的數據,否則用空表示。
(2),使用Linq to XML查詢XML文件
在Linq提出之前, 我們可以使用XPath來查詢XML文件, 但是用XPath時必須首先知道XML文件的具體結構, 而使用Linq to XML則不需要知道這些.
而且Linq to XML的代碼還更加簡潔.

1 class Program 2 { 3 //初始化xml數據 4 private static string xmlString = 5 "<Persons>" + 6 "<Person Id = '1'>" + 7 "<Name>Barry Wang</Name>" + 8 "<Age>18</Age>" + 9 "</Person>" + 10 "<Person Id = '2'>" + 11 "<Name>Tony Jia</Name>" + 12 "<Age>20</Age>" + 13 "</Person>" + 14 "<Person Id = '3'>" + 15 "<Name>Anson Shen</Name>" + 16 "<Age>19</Age>" + 17 "</Person>" + 18 "</Persons>"; 19 20 21 static void Main(string[] args) 22 { 23 Console.WriteLine("使用Linq方法來對XML文件查詢, 查詢結果是: "); 24 UsingLinqLinqToXmlQuery(); 25 Console.ReadKey(); 26 } 27 28 //使用Linq來對XML文件進行查詢 29 private static void UsingLinqLinqToXmlQuery() 30 { 31 //導入XML文件 32 XElement xmlDoc = XElement.Parse(xmlString); 33 34 //創建查詢, 獲取姓名為"李四"的元素 35 var queryResults = from element in xmlDoc.Elements("Person") 36 where element.Element("Name").Value == "Barry Wang" 37 select element; 38 39 //輸出查詢結果 40 foreach (var xele in queryResults) 41 { 42 Console.WriteLine("姓名為: " + xele.Element("Name").Value + "Id為: " + xele.Attribute("Id").Value); 43 } 44 } 45 }
Linq to DataSet其實都和Linq to Object 類似, 這里就不在講解了.更多內容在以下兩個鏈接:
MSDN之Linq講解
Linq操作合集
2,Lambda表達式
Lambda表達式可以理解為一個匿名方法, 它可以包含表達式和語句, 並且用於創建委托或轉換表達式樹.
在使用Lambda表示式時, 都會使用"=>"運算符(讀作goes to), 該運算符的左邊是匿名方法的輸入參數, 右邊則是表達式或語句塊.
這里主要列舉下Linq和Lambda表達式的一些區別:
LINQ的書寫格式如下:
from 臨時變量 in 集合對象或數據庫對象
where 條件表達式
[order by條件]
select 臨時變量中被查詢的值
[group by 條件]
Lambda表達式的書寫格式如下:
(參數列表) => 表達式或者語句塊
其中:參數個數:可以有多個參數,一個參數,或者無參數。
參數類型:可以隱式或者顯式定義。
表達式或者語句塊:這部分就是我們平常寫函數的實現部分(函數體)。
1.查詢全部
查詢Student表的所有記錄。

1 select * from student 2 Linq: 3 from s in Students 4 select s 5 Lambda: 6 Students.Select( s => s)
2 按條件查詢全部:
查詢Student表中的所有記錄的Sname、Ssex和Class列。

1 select sname,ssex,class from student 2 Linq: 3 from s in Students 4 select new { 5 s.SNAME, 6 s.SSEX, 7 s.CLASS 8 } 9 Lambda: 10 Students.Select( s => new { 11 SNAME = s.SNAME,SSEX = s.SSEX,CLASS = s.CLASS 12 })
3.distinct 去掉重復的
查詢教師所有的單位即不重復的Depart列。

1 select distinct depart from teacher 2 Linq: 3 from t in Teachers.Distinct() 4 select t.DEPART 5 Lambda: 6 Teachers.Distinct().Select( t => t.DEPART)
4.連接查詢 between and
查詢Score表中成績在60到80之間的所有記錄。

1 select * from score where degree between 60 and 80 2 Linq: 3 from s in Scores 4 where s.DEGREE >= 60 && s.DEGREE < 80 5 select s 6 Lambda: 7 Scores.Where( 8 s => ( 9 s.DEGREE >= 60 && s.DEGREE < 80 10 ) 11 )
5.在范圍內篩選 In

1 select * from score where degree in (85,86,88) 2 Linq: 3 from s in Scores 4 where ( 5 new decimal[]{85,86,88} 6 ).Contains(s.DEGREE) 7 select s 8 Lambda: 9 Scores.Where( s => new Decimal[] {85,86,88}.Contains(s.DEGREE))
6.or 條件過濾
查詢Student表中"95031"班或性別為"女"的同學記錄。

1 select * from student where class ='95031' or ssex= N'女' 2 Linq: 3 from s in Students 4 where s.CLASS == "95031" 5 || s.CLASS == "女" 6 select s 7 Lambda: 8 Students.Where(s => ( s.CLASS == "95031" || s.CLASS == "女"))
7.排序
以Class降序查詢Student表的所有記錄。

1 select * from student order by Class DESC 2 Linq: 3 from s in Students 4 orderby s.CLASS descending 5 select s 6 Lambda: 7 Students.OrderByDescending(s => s.CLASS)
8.count()行數查詢

1 select count(*) from student where class = '95031' 2 Linq: 3 ( from s in Students 4 where s.CLASS == "95031" 5 select s 6 ).Count() 7 Lambda: 8 Students.Where( s => s.CLASS == "95031" ) 9 .Select( s => s) 10 .Count()
9.avg()平均
查詢'3-105'號課程的平均分。

1 select avg(degree) from score where cno = '3-105' 2 Linq: 3 ( 4 from s in Scores 5 where s.CNO == "3-105" 6 select s.DEGREE 7 ).Average() 8 Lambda: 9 Scores.Where( s => s.CNO == "3-105") 10 .Select( s => s.DEGREE).Average()
10.子查詢
查詢Score表中的最高分的學生學號和課程號。

1 select distinct s.Sno,c.Cno from student as s,course as c ,score as sc 2 where s.sno=(select sno from score where degree = (select max(degree) from score)) 3 and c.cno = (select cno from score where degree = (select max(degree) from score)) 4 Linq: 5 ( 6 from s in Students 7 from c in Courses 8 from sc in Scores 9 let maxDegree = (from sss in Scores 10 select sss.DEGREE 11 ).Max() 12 let sno = (from ss in Scores 13 where ss.DEGREE == maxDegree 14 select ss.SNO).Single().ToString() 15 let cno = (from ssss in Scores 16 where ssss.DEGREE == maxDegree 17 select ssss.CNO).Single().ToString() 18 where s.SNO == sno && c.CNO == cno 19 select new { 20 s.SNO, 21 c.CNO 22 } 23 ).Distinct()
11.分組 過濾
查詢Score表中至少有5名學生選修的並以3開頭的課程的平均分數。

1 select avg(degree) from score where cno like '3%' group by Cno having count(*)>=5 2 Linq: 3 from s in Scores 4 where s.CNO.StartsWith("3") 5 group s by s.CNO 6 into cc 7 where cc.Count() >= 5 8 select cc.Average( c => c.DEGREE) 9 Lambda: 10 Scores.Where( s => s.CNO.StartsWith("3") ) 11 .GroupBy( s => s.CNO ) 12 .Where( cc => ( cc.Count() >= 5) ) 13 .Select( cc => cc.Average( c => c.DEGREE) ) 14 Linq: SqlMethod 15 like也可以這樣寫: 16 s.CNO.StartsWith("3") or SqlMethods.Like(s.CNO,"%3")
12.分組
查詢Score表中至少有5名學生選修的並以3開頭的課程的平均分數。

1 select avg(degree) from score where cno like '3%' group by Cno having count(*)>=5 2 Linq: 3 from s in Scores 4 where s.CNO.StartsWith("3") 5 group s by s.CNO 6 into cc 7 where cc.Count() >= 5 8 select cc.Average( c => c.DEGREE) 9 Lambda: 10 Scores.Where( s => s.CNO.StartsWith("3") ) 11 .GroupBy( s => s.CNO ) 12 .Where( cc => ( cc.Count() >= 5) ) 13 .Select( cc => cc.Average( c => c.DEGREE) ) 14 Linq: SqlMethod 15 like也可以這樣寫: 16 s.CNO.StartsWith("3") or SqlMethods.Like(s.CNO,"%3")
13. 多表查詢

1 select sc.sno,c.cname,sc.degree from course as c,score as sc where c.cno = sc.cno 2 Linq: 3 from c in Courses 4 join sc in Scores 5 on c.CNO equals sc.CNO 6 select new 7 { 8 sc.SNO,c.CNAME,sc.DEGREE 9 } 10 Lambda: 11 Courses.Join ( Scores, c => c.CNO, 12 sc => sc.CNO, 13 (c, sc) => new 14 { 15 SNO = sc.SNO, 16 CNAME = c.CNAME, 17 DEGREE = sc.DEGREE 18 })
14,關聯多條件查詢
感謝@浪子哥 給的建議, 現在加上兩張表的關聯多條件查詢, 只有Linq和Lambda表達式
今天自己又參考園里大神的一些帖子自己寫了一個兩張表關聯查詢的Linq及Lambda表達式的Demo, 大家可以看下.
1 class Program 2 { 3 static void Main() 4 { 5 DataTable tableA = new DataTable(); 6 tableA.Columns.Add("Name", typeof(string)); 7 tableA.Columns.Add("Age", typeof(int)); 8 tableA.Columns.Add("Score", typeof(int)); 9 10 DataTable tableB = new DataTable(); 11 tableB.Columns.Add("Name", typeof(string)); 12 tableB.Columns.Add("Age", typeof(int)); 13 tableB.Columns.Add("Score", typeof(int)); 14 15 tableA.Rows.Add("Barry", 12, 60); 16 tableA.Rows.Add("Tony", 24, 70); 17 tableA.Rows.Add("Tom", 22, 80); 18 tableA.Rows.Add("Brad", 25, 90); 19 20 tableB.Rows.Add("Kitty", 12, 65); 21 tableB.Rows.Add("Tom", 27, 75); 22 tableB.Rows.Add("Jhon", 22, 85); 23 tableB.Rows.Add("Brad", 21, 95); 24 25 //單表查詢 26 var singleQuery = tableA.AsEnumerable().Where(stu => stu.Field<int>("Age") > 20); 27 foreach (var item in singleQuery) 28 { 29 Console.WriteLine("Case 1 query result: Age {0}, Name {1}", item.Field<int>("Age"),
item.Field<string>("Name").ToString()); 30 } 31 32 //Linq:兩張表的關聯查詢 33 var doubleQuery = from a in tableA.AsEnumerable() 34 from b in tableB.AsEnumerable() 35 where a.Field<string>("Name") == b.Field<string>("Name") && 36 a.Field<int>("Age") != b.Field<int>("Age") 37 orderby a.Field<string>("Name"), a.Field<int>("Score") 38 select new 39 { 40 Name = a.Field<string>("Name"), 41 A_Age = a.Field<int>("Age"), 42 B_Age = b.Field<int>("Age") 43 }; 44 foreach (var item in doubleQuery) 45 { 46 Console.WriteLine("Case 2 query result: Name {0}, tableA_Age {1}, tableB_Age {2}",
item.Name, item.A_Age, item.B_Age); 47 } 48 49 //Lambda:兩張表的關聯查詢 50 var query = tableA.AsEnumerable() 51 .Join(tableB.AsEnumerable(), 52 a => a.Field<string>("Name"), 53 b => b.Field<string>("Name"), 54 (a, b) => new 55 { 56 a = a, 57 b = b 58 }) 59 .Where(c => (c.a.Field<int>("Age") != c.b.Field<int>("Age"))) 60 .OrderBy(d => d.a.Field<string>("Name")) 61 .ThenBy(e => e.a.Field<int>("Score")) 62 .Select(f => new 63 { 64 Name = f.a.Field<string>("Name"), 65 A_Age = f.a.Field<int>("Age"), 66 B_Age = f.b.Field<int>("Age"), 67 A_Score = f.a.Field<int>("Score"), 68 B_Score = f.a.Field<int>("Score") 69 }); 70 foreach (var item in query) 71 { 72 Console.WriteLine("Case 3 query result: Name {0}, tableA_Age {1}, tableB_Age {2}, tableA_Score {3},
tableB_Score {4}",item.Name, item.A_Age, item.B_Age, item.A_Score, item.B_Score);
73 } 74 Console.ReadKey(); 75 } 76 }
解析:
首先可以看出來, Lambda表達式對於這種多表多條件的查詢寫法的易讀性明顯沒有Linq高, 所以 還是建議用Linq去寫. 運行結果如下圖: