LINQ 常規實踐

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

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