參考資料:[https://ithelp.ithome.com.tw/users/20009278/ironman/450](https://ithelp.ithome.com.tw/users/20009278/ironman/450) # 表達式 ## Select 查詢 ### select ![](https://hackmd.io/_uploads/HJyBc93GT.png) ``` //LINQ查詢表達式 var 變數名稱 = from 資料名稱 in 資料表 where 條件 select 欄位名稱; //範例1 //LINQ查詢表達式 var query = from c in Customers where c.City == "London" select new {c.CustomerID, c.CompanyName, c.City}; //執行LINQ查詢 query.Dump(); //範例2 //資料來源 string[] words = { "Hello", "wonderful", "LINQ", "beautiful", "World" }; //LINQ查詢表達式 var shortWords = from word in words where word.Length <= 5 select word; //顯示查詢結果 foreach (var e in shortWords) { Console.WriteLine(e.ToString()); } ``` ### select Many SelectMany 運算子會先做和 Select 相同的事,但Select 運算子只要取得第一層的查詢結果就會放到輸出序列中,但是 SelectMany 若發現查詢結果回傳的是一個可以列舉的序列,則會再進一步把這個序列中的項目取出來,放到輸出序列中 | select | select many | | -------- | -------- | | ![](https://hackmd.io/_uploads/r1VEic2MT.png)|![](https://hackmd.io/_uploads/rJ3Ni92M6.png)| ``` string[] ary = new string[]{"zero", "one", "two", "three"}; var querySelect = ary.Select(a => a); var querySelectMany = ary.SelectMany(a => a); querySelect.Dump("Select result", false); querySelectMany.Dump("SelectMany result", false); ``` | select | select many | | -------- | -------- | |![](https://hackmd.io/_uploads/Hye0T92fa.png)| ![](https://hackmd.io/_uploads/By6A653M6.png) | ``` string[] ary = new string[]{"zero", "one", "two", "three"}; var querySelect = ary.Select(a => a.Split('r')); var querySelectMany = ary.SelectMany(a => a.Split('r')); querySelect.Dump("Select result", false); querySelectMany.Dump("SelectMany result", false); ``` ## Insert ``` db.tablename.add(item) db.SaveChanges();//這行一定要 ``` ## Delete ``` db.tablename.Remove(item); db.SaveChanges(); <!--當按下按鈕的時候刪除--> var userDbRes = db.tablename.Where(w => w.tablename.Equals(StoreIdlist)); foreach (var item in userDbRes) { // 刪除角色關聯 db.tablename.Remove(item); } db.SaveChanges();//commit ``` ## Revise 和insert很像不過要和delete一樣先抓db裏的資料 ``` User u = db.tablename.Where(w => w.id==Equals(id)) u.id=id; u.name=name; db.SaveChanges(); ``` ## where OfType\<TResult> 是屬於延後執行的運算子,所以在 Foreach 之前,我們還可以再更改資料來源的內容。 OfType\<TResult> 只會取回符合型別的資料,但這個符合型別的定義,包含可以隱含轉換的型別,例如:類別子系(繼承關係) ``` void Main() { //請注意:ArrayList 並未實做 IEnumerable<T>,所以不能應用 LINQ ArrayList ary = new ArrayList(); //加入三個顧客資料 ary.Add(new Customer {Id = 1, Name = "Leo", Age = 36}); ary.Add(new Customer {Id = 2, Name = "Rose", Age = 28}); ary.Add(new Customer {Id = 3, Name = "Alvin", Age = 2}); //加入三個顧客的訂單資料 ary.Add(new Order {CustomerId = 3, OrderDate = new DateTime(2011, 10, 9), Total = 2940}); ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 10, 10), Total = 3849}); ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2011, 12, 1), Total = 500}); ary.Add(new Order {CustomerId = 1, OrderDate = new DateTime(2012, 2, 28), Total = 1234}); ary.Add(new Order {CustomerId = 2, OrderDate = new DateTime(2012, 5, 20), Total = 9520}); //透過 OfType 過濾出訂單資料,並用 Where 條件查出小於千元的訂單 var queryOrder = from e in ary.OfType<Order>() where e.Total < 1000 select e; foreach (var e in queryOrder) { Console.WriteLine(e.ToString()); } } //顧客基本資料類別 public class Customer { public int Id {get; set;} public string Name {get; set;} public int Age { get; set; } public override string ToString() { return string.Format("Id = {0}, Name = {1}, Age = {2}", Id, Name, Age); } } //訂單基本資料類別 public class Order { public int CustomerId { get; set; } public DateTime OrderDate { get; set; } public double Total { get; set; } public override string ToString() { return string.Format("CustomerId = {0}, OrderDate = {1}, Total = {2}", CustomerId, OrderDate, Total); } } //輸出: //CustomerId = 1, OrderDate = 2011/12/1 上午 12:00:00, Total = 500 ``` ## 單一運算子 在 LINQ 技術中,有設計一組專門用來取出來源序列中單一項目的運算子,也就是 First、Last、ElementAt、Single,它們同時也都搭配了一組取不到資料就輸出來源資料型別預設值的運算子:FirstOrDefault、LastOrDefault、ElementAtOrDefault、SingleOrDefault ### First 最常用、最通用的就是 First、FirstOrDefault 運算子,它們會回傳來源序列中的第一個元素,或符合條件的第一個元素。它們都有兩個多載方法: ``` public static TSource First<TSource>( this IEnumerable<TSource> source ) public static TSource First<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate ) public static TSource FirstOrDefault<TSource>( this IEnumerable<TSource> source ) public static TSource FirstOrDefault<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate ) ``` ### Last Last、LastOrDefault 運算子和 First、FirstOrDefault 用法完全相同,只是這組運算子是取來源序的最後一筆資料 ``` public static TSource Last<TSource>( this IEnumerable<TSource> source ) public static TSource Last<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate ) public static TSource LastOrDefault<TSource>( this IEnumerable<TSource> source ) public static TSource LastOrDefault<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate ) ``` ## 分頁方式 ### Take Take 運算子是擴充自 IEnumerable\<TSource> 的方法,只需傳入一個 int 型別的參數,表示要取回的項目個數。當 Take 運算子被調用時,會從來源序列的第一個項目開始,依序取回指定數量的項目當做結果回傳。 ``` var list = new List<int>() {68,50,15,55,13, 0,81,29,40,10, 9,64,12,74,90, 25,4,77,53,42, 49,31,26}; var query = list.OrderBy(l => l).Take(5); query.Dump(); /* 輸出: 0 4 9 10 12 */ ``` ### Skip Skip 運算子和 Take 運算子正好相反,它可用來跳過來源序列中的前幾個項目,再把剩下的資料全部回傳 ``` var list = new List<int>() {68,50,15,55,13, 0,81,29,40,10, 9,64,12,74,90, 25,4,77,53,42, 49,31,26}; var query = list.OrderBy(l => l).Skip(20); query.Dump(); /* 輸出: 77 81 90 */ ``` ### TakeWhile TakeWhile 運算子,在取資料前,會先把來源序列的項目丟給委派參數 predicate 並判斷回傳值為 true 時,才取出來,要注意它的特性是:一旦 predicate 回傳 false,就會中止查詢作業,回傳到目前為止的處理結果 ``` int[] numbers = { 33, 60, 222, 34, 999, 1 }; numbers.TakeWhile(n => n < 100).Dump("TakeWhile"); /* 輸出: 33 60 */ ``` ### SkipWhile SkipWhile 運算子,會依序把來源序列的每個項目傳給 predicate 委派判斷,若回傳 true,就跳過,一直到 predicate 回傳 false 時,就會把剩餘的來源序列之項目全部回傳 ``` int[] numbers = { 33, 60, 222, 34, 999, 1 }; numbers.SkipWhile((n, idx) => n < 100 || n > (idx * 200)).Dump("SkipWhile"); /* 輸出: 222 34 999 1 */ ``` predicate 的邏輯是,只要傳入項目小於 100,或者大於索引值 * 200,都跳過。最後輸出結果是跳過前兩個項目,因為第三個項目 222 大於 100,造成 predicate 回傳 false,所以系統就會把剩餘的來源序列中之項目統統回傳 ## 設定方法 ### Any Any 運算子,只要序列不是空的(至少有一個項目),就會回傳 true,反之回傳 false ``` var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba","IBM","HP","Dell"}; var emptyList = new List<string>() {}; Console.WriteLine(list.Any()); Console.WriteLine(emptyList.Any()); /* 輸出: True False */ ``` 第二個多載方法則是可以讓我們傳入一個委派 predicate,自行設定條件,然後來源序列中其中一個項目符合才為真(true) ``` var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba","IBM","HP","Dell"}; //第一個查詢檢查序列中是否有 Acer 這個廠牌名稱 Console.WriteLine(list.Any (l => l == "Acer")); //第二個查詢檢查序列中項目,是否有任何一個長度大於 10 Console.WriteLine(list.Any (l => l.Length > 10)); /* 輸出: True False */ ``` ### All All 運算子要求我們必須傳入一個委派 predicate,自行設定條件,然後來源序列中所有項目都必須符合才為真(true) p.s.來源序列是空的(沒有任何項目),則不管設定的條件為何,回傳都是真(true) ``` var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba", "Dell"}; //第一個是設定所有項目的長度是否都大於 3 Console.WriteLine(list.All(l => l.Length > 3)); //第二個查詢則是檢查序列中所有項目是否都有包含 "A" Console.WriteLine(list.All(l => l.Contains("A"))); /* 輸出: True False */ var emptyList = new List<string>() {}; Console.WriteLine(emptyList.All(l => l.Contains("A"))); /* 輸出: True */ ``` ### Contains 它用來判斷序列中是否包含指定的項目 ``` var list = new List<string>() {"ASUS","Acer","BenQ", "Toshiba", "Dell"}; Console.WriteLine(list.Contains("Acer")); Console.WriteLine(list.Contains("A")); /* 輸出: True False */ ``` ## 標準查詢子 ### Range Range 運算子可以讓我們產生指定範圍內的「整數」序列 傳入兩個 int 型別參數,第一個是整數序列的起始值,第二個參數是【數量】 ``` public static IEnumerable<int> Range( int start, int count ) var squares = Enumerable.Range(2, 4).Select(i => i * i); foreach (var num in squares) { Console.WriteLine(num); } Console.WriteLine(squares.GetType()); /* 輸出: 4 9 16 25 typeof (IEnumerable<Int32>) */ ``` ### Repeat Repeat 運算子可以讓我們產生一個序列,包含指定數量的元素(或稱之為項目) ``` public static IEnumerable<TResult> Repeat<TResult>( TResult element, int count ) var echoes = Enumerable.Repeat("Hello~ ", 3); foreach (var s in echoes) { Console.WriteLine(s); } Console.WriteLine(echoes.GetType()); /* 輸出: Hello~ Hello~ Hello~ typeof (IEnumerable<String>) */ ``` ### Empty Empty 運算子可以讓我們建立一個包含指定型別的空序列 ``` IEnumerable<Decimal> pays = Enumerable.Empty<Decimal>(); Console.WriteLine(pays.GetType()); // 輸出:typeof (Decimal[]) ``` ### Distinct Distinct 運算子,它會依序把來源序列中的每一個項目,拿來和輸出序列中的項目做比較,若已存在於來源序列中,則排除,反之,則加入準備回傳的輸出序列裡 ``` public static IEnumerable<TSource> Distinct<TSource>( this IEnumerable<TSource> source ) public static IEnumerable<TSource> Distinct<TSource>( this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer ) ``` ### Join Join 運算子的邏輯,大致上是這樣: 依序將 inner 所有項目(TInner)取出來,傳給 innerKeySelecor 產生 TKey,然後存到一個 Hashtable 中。 依序將 outer 所有項目(TOuter)取出來,傳給 outerKeySelector 產生 TKey,到步驟 1 產生的 Hashtable 查詢是否有相符的項目。不符合就重覆步驟 2 (取 outer 中下一個 TOuter),符合就進行步驟 3。 符合的話,就把 TOuter 和相符的 TInner 當做參數,傳給 resultSelector,取回 TResult,指定到準備輸出的 IEnumerable\<TResult> 中。 範例資料 ![](https://hackmd.io/_uploads/r1iaGp2MT.png) ``` void Main() { var Customers = DataProvider.getCustomers(); var Orders = DataProvider.getOrders(); Customers.Dump(); Orders.Dump(); } public static class DataProvider { public static List<Customer> getCustomers() { var Customers = new List<Customer> { new Customer {ID = 1, Name = "Leo"}, new Customer {ID = 2, Name = "Rose"}, new Customer {ID = 3, Name = "Alvin"}, new Customer {ID = 4, Name = "Emy"}, new Customer {ID = 5, Name = "Alice"}, new Customer {ID = 6, Name = "Bobo"} }; return Customers; } public static List<Order> getOrders() { var Orders = new List<Order> { new Order {ID = 1, CustomerID = 1, Date = new DateTime(2012,1,5), Description = "Mouse", Price = 480}, new Order {ID = 2, CustomerID = 1, Date = new DateTime(2012,2,15), Description = "Books", Price = 880}, new Order {ID = 3, CustomerID = 2, Date = new DateTime(2011,6,16), Description = "Keyboard", Price = 290}, new Order {ID = 4, CustomerID = 2, Date = new DateTime(2012,3,25), Description = "NoteBook", Price = 16800}, new Order {ID = 5, CustomerID = 3, Date = new DateTime(2012,8,15), Description = "Mouse", Price = 480}, new Order {ID = 6, CustomerID = 4, Date = new DateTime(2011,6,22), Description = "NoteBook", Price = 16800}, new Order {ID = 7, CustomerID = 4, Date = new DateTime(2011,10,10), Description = "Mouse", Price = 480}, new Order {ID = 8, CustomerID = 4, Date = new DateTime(2012,9,8), Description = "Camera", Price = 29900}, }; return Orders; } } public class Customer { public int ID { get; set; } public string Name { get; set; } } public class Order { public int ID { get; set; } public int CustomerID { get; set; } public DateTime Date { get; set; } public string Description { get; set; } public Decimal Price { get; set; } } ``` join範例 ``` void Main() { var Customers = DataProvider.getCustomers(); var Orders = DataProvider.getOrders(); //全宣告一個query,放c(Customer)的資料,加上o(Orders)來的資料,條件是id相同 var query = from c in Customers join o in Orders on c.ID equals o.CustomerID select c.Name + " 買了 " + o.Description; query.Dump(); } ``` 多個join串列 ![](https://hackmd.io/_uploads/Hk3hET2z6.png) ``` public class OrderItem { public int ID { get; set; } public int OrderID { get; set; } public string Detail { get; set; } } void Main() { var Customers = DataProvider.getCustomers(); var Orders = DataProvider.getOrders(); List<OrderItem> OrderItems = new List<OrderItem> { new OrderItem {ID = 1, OrderID = 4, Detail = "AAA"}, new OrderItem {ID = 2, OrderID = 4, Detail = "BBB"}, new OrderItem {ID = 3, OrderID = 2, Detail = "CCC"}, new OrderItem {ID = 3, OrderID = 2, Detail = "DDD"}, new OrderItem {ID = 3, OrderID = 5, Detail = "EEE"} }; var query = from c in Customers join o in Orders on c.ID equals o.CustomerID join oi in OrderItems on o.ID equals oi.OrderID select new { c.Name, o.Description, oi.Detail }; query.Dump(); }s ``` ## Aggregate 範例資料 ``` int[] Source = new int[] { 2, 7, 5, 1, 6, 8, 3 }; ``` ### Sum ``` private static int Sum(IEnumerable<int> source) { //return source.Sum(x => x); return source.Aggregate((total, next) => total + next); } ``` ### Min 每次都去比對目前的元素是否小於目前的最小值,如果是的話就更新最小值 ``` private static int Min(IEnumerable<int> source) { //return source.Min(x => x); return source.Aggregate((min, next) => min > next ? next : min); } ``` ### Max 每次都去比對目前的元素是否大於目前的最大值,如果是的話就更新最大值 ``` private static int Max(IEnumerable<int> source) { //return source.Max(x => x); return source.Aggregate((max, next) => max < next ? next : max); } ``` ### Count 以0為起始彙整資料,每個元素都累加1 ``` private static int Count(IEnumerable<int> source) { //return source.Count(); return source.Aggregate(0, (count, next) => ++count); } ``` ### Average 累加元素並對外部變數count加1,最後用resultSelector取得平均資料 ``` private static double Average(IEnumerable<int> source) { int count = 0; //return source.Average(x => x); return source.Aggregate( 0, (total, next) => { total = total + next; count++; return total; }, total => (double)total / count ); } ``` ## 載入 ### 延遲載入 延遲載入:執行程式取出每一個元素都會執行LINQ ![](https://hackmd.io/_uploads/SkVMw9hzT.png) ``` //初始化資料來源 int[] intAry = new int[]{0, 1, 2}; //建立查詢 var query = from n in intAry select n; //第一次執行查詢 query.Dump("第一次查詢-原始資料"); //修改資料來源 intAry[0] = 99; intAry[2] = 97; //第二次執行查詢(並未異動查詢語法) query.Dump("第二次查詢-延後執行的效果"); ``` ### 預先載入 預先載入:在執行程式前,已執行完LINQ的查詢 ![](https://hackmd.io/_uploads/B1KQDq3z6.png) ``` //初始化資料來源 var list = new List<int>(){1,5,10}; //建立查詢呼叫加總(Sum)運算子 var querySum = (from n in list select n).Sum (c => c); //第一次執行查詢 querySum.Dump("第一次查詢-原始資料加總"); //修改資料來源 list.Add(99); //第二次執行查詢(並未異動查詢語法) querySum.Dump("第二次查詢"); //重新定義 LINQ 查詢子句 querySum = (from n in list select n).Sum (c => c); querySum.Dump("第三次查詢"); ```