1.Linq指定多列排序
OrderBy的意義是按照指定順序排序,連續兩次OrderBy,後面一個有可能會打亂前面一個的排序順序,可能與預期不符。
要實現sql中的order by word,name類似效果; LINQ 有ThenBy可以緊接使用, ThenBy記住原本排序的值,然後再排其他值, 正因如此,ThenBy是針對IOrderEnumerable 進行調用的。
2. Linq主外鍵連接查詢
group join操作符常用於返回‘主鍵對象-外鍵對象集合’的查詢,例如‘產品類別-此類別下所有的產品’的模式。
// 查詢語法 var query = from c in db.Categories join p in db.Products on c.CategoryID equals p.CategoryID into r select new { c.CategoryName, Products = r }; // 方法語法 var q = db.Categories .GroupJoin ( db.Products, c => c.CategoryID, p => p.CategoryID, (c, p) => new { c.CategoryName, Products = p } ); 這樣就可以結合 DefaultIfEmpty 理解 left outer join的linq寫法。
group join生成的sql接近於:
select Categories.*, Products.* from Categories left join Products on Categories.Id = Products.CateId order by Categories.Id // group join 有點類似於 left join 數據膨脹的效果
https://docs.microsoft.com/en-us/dotnet/csharp/linq/perform-grouped-joins
3. Linq2SQL 生成的sql指定左右連接
linq2sql join語法默認得到的是inner join
Model1Container model = new Model1Container(); //內連接 var query = from s in model.Student join c in model.Course on s.CourseCno equals c.Cno where c.Cno == 1 select new { ClassID = s.CourseCno, ClassName = c.Cname, Student = new { Name = s.Sname, ID = s.Sno } }; foreach (var item in query) { Response.Write("ClassID:" + item.ClassID + "ClassName:" + item.ClassName + "Name:" + item.Student.Name); }
在sql profile裏面監控到與上面Linq2sql 對應的sql是
SELECT [t0].[CourseCno] AS [ClassID], [t1].[Cname] AS [ClassName], [t0].[Sname] AS [Name], [t0].[Sno] AS [ID] FROM [Student] AS [t0] INNER JOIN [Course] AS [t1] ON [t0].[CourseCno] = [t1].[Cno] WHERE [t1].[Cno] = @p0
linq2sql 左連接
Model1Container model = new Model1Container(); var query = from s in model.Student join c in model.Course on s.CourseCno equals c.Cno into gc from gci in gc.DefaultIfEmpty() select new { ClassID = s.CourseCno, ClassName = gci ==null ?String.Empty: gci.Cname, Student = new { Name = s.Sname, ID = s.Sno } }; //Outer join時必須將join後的表into到一個新的變量gc中,然後要用gc.DefaultIfEmpty()表示外連接(沒有匹配的記錄字段設爲NULL) foreach (var item in query) { Response.Write("ClassID:" + item.ClassID + "ClassName:" + item.ClassName + "Name:" + item.Student.Name); } // 上例中使用了DefaultIfEmpty操作符,它能夠爲Empty序列(注意是Empty序列而不是Null序列)返回一個默認元素序列。DefaultIfEmpty使用了泛型中的default關鍵字。default關鍵字對於引用類型將返回null,而對於值類型則返回0。對於結構體類型,則會根據其成員類型將它們相應地初始化爲null(引用類型)或0(值類型) gc.DefaultIfEmpty(new Course { Cname = "",Cperiod="" } ) //設置爲空時的默認值
與以上Linq2sql對應的sql 是
SELECT [t0].[CourseCno] AS [ClassID], [t1].[Cname] AS [ClassName], [t0].[Sname] AS [Name], [t0].[Sno] AS [ID] FROM [Student] AS [t0] LEFT OUTER JOIN [Course] AS [t1] ON [t0].[CourseCno] = [t1].[Cno]
https://docs.microsoft.com/en-us/dotnet/csharp/linq/perform-left-outer-joins
4. 忽略Linq 檢索某元素異常
在日常Linq實踐中,按照某種預期寫定的Linq, 在執行檢索時某些元素會爆出異常,導致整個檢索失敗;如果我們能容忍某些元素的異常,繼續完成整個Linq檢索,可以寫一個擴展方法,忽略報錯元素。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Enumerable { public static class EnumerableExtension { /// <summary> /// 在IEnumerable<T> 循環或者變爲內存序列之前 忽略掉序列中存在的元素異常 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="values"></param> /// <returns></returns> public static IEnumerable<T> SkipException<T> (this IEnumerable<T> values) { using(var enumerator = values.GetEnumerator()) { var next = true; while(next) { try{ // 如果枚舉器成功推進到下一個元素,MoveNext爲true;枚舉數越過集合結尾,則爲false,也就是說返回值只確定後續是否有值 next = enumerator.MoveNext(); } catch{ // catch到異常,忽略當前元素,繼續循環 LogHelper.Write("cause exception", LogHelper.LogMessageType.Error); continue; } // yield 返回枚舉器指向的當前元素 if(next) yield return enumerator.Current; } } } } }
這個擴展方法的思路是: 重寫foreach檢索語法的默認邏輯。
When you use the yield keyword in a statement, you indicate that the method, operator, or get accessor in which it appears is an iterator.
You use a yield return statement to return each element one at a time.
You consume an iterator method by using a foreach statement or LINQ query