2020-05-14 - Linq 查詢

  • Entity Framework Core 使用語(yǔ)言集成查詢 (LINQ) 來(lái)查詢數(shù)據(jù)庫(kù)中的數(shù)據(jù)。

https://docs.microsoft.com/zh-cn/ef/core/querying/

  • 客戶端與服務(wù)器端評(píng)估

    • Entity Framework Core 會(huì)嘗試盡可能在服務(wù)器上查詢。
    • EF Core 支持在頂級(jí)投影中進(jìn)行部分客戶端評(píng)估(基本上為最后一次調(diào)用 Select())。 如果查詢中的頂級(jí)投影無(wú)法轉(zhuǎn)換為服務(wù)器,EF Core 將從服務(wù)器中提取任何所需的數(shù)據(jù),并在客戶端上評(píng)估查詢的其余部分。

      指最后一次Select時(shí), 如果遇到服務(wù)器不認(rèn)識(shí)的轉(zhuǎn)換,則將可以在客戶端進(jìn)行轉(zhuǎn)換
      * EF Core 在頂級(jí)投影之外的任何位置檢測(cè)到不能轉(zhuǎn)換為服務(wù)器的表達(dá)式,則會(huì)引發(fā)運(yùn)行時(shí)異常。
      > 即在最后一次 Select 之前,如果遇到不能轉(zhuǎn)換的表達(dá)式,則拋出異常

    • 頂級(jí)投影中的客戶端評(píng)估
      var blogs = context.Blogs .Select(blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) }) .ToList();
      上面代碼中, 最后一個(gè)Select中的StandardizeUrl 函數(shù)不被服務(wù)器認(rèn)識(shí),所以將先在服務(wù)器查詢所有數(shù)據(jù),然后在客戶端轉(zhuǎn)換
    • 不支持的客戶端評(píng)估
      var blogs = context.Blogs .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) .ToList();
      上面代碼中, ToList為最后一次 Select, 而之前的 where 中包含不被服務(wù)器認(rèn)識(shí)的函數(shù) StandardizeUrl, 所以將拋出異常
    • 顯式客戶端評(píng)估
      a. 由于數(shù)據(jù)量小,因此在進(jìn)行客戶端評(píng)估時(shí)才不會(huì)大幅減弱性能。
      b. 所用的 LINQ 運(yùn)算符不會(huì)進(jìn)行任何服務(wù)器端轉(zhuǎn)換
      c. 通過(guò)調(diào)用 AsEnumerable 或 ToList 等方法(若為異步,則調(diào)用 AsAsyncEnumerable 或 ToListAsync),以顯式方式選擇進(jìn)行客戶端評(píng)估
      d. AsEnumerable 將對(duì)結(jié)果進(jìn)行流式傳輸, 而 ToList 會(huì)占用額外的內(nèi)存
      var blogs = context.Blogs .AsEnumerable() .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) .ToList();
      上面代碼中, AsEnumerable 為最后一次 Select,
    • 內(nèi)存泄漏
      在 Linq查詢中,使用了實(shí)例中的常數(shù), 而 Linq查詢 會(huì)緩存表達(dá)式, 這使得實(shí)例無(wú)法釋放, 因?yàn)槠浔痪彺嫠褂?
  • 跟蹤與非跟蹤查詢

    • 非跟蹤查詢
      var blogs = context.Blogs.AsNoTracking().ToList(); 或者變更整個(gè) DbContext 為不跟蹤
      context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    • 標(biāo)識(shí)解析

    EF Core 將在跟蹤查詢中執(zhí)行標(biāo)識(shí)解析。 當(dāng)具體化實(shí)體時(shí),如果 EF Core 已被跟蹤,則會(huì)從更改跟蹤器返回相同的實(shí)體實(shí)例。 如果結(jié)果中多次包含相同的實(shí)體,則每次會(huì)返回相同的實(shí)例。

    非跟蹤查詢不會(huì)使用更改跟蹤器,也不會(huì)執(zhí)行標(biāo)識(shí)解析。 因此會(huì)返回實(shí)體的新實(shí)例

  • 復(fù)雜查詢

    • join -- 連接2個(gè)表, 有關(guān)聯(lián)比較條件

    將使用 inner join 關(guān)聯(lián)2個(gè)表, 這個(gè)需要有關(guān)聯(lián)條件

var query = from photo in context.Set<PersonPhoto>()
                    join person in context.Set<Person>() on photo.PersonPhotoId equals person.PhotoId
                   select new { person, photo }
  • groupjoin -- 不支持,將拋出異常

  • SelectMany -- 連接2個(gè)表, 無(wú)關(guān)聯(lián)條件

a. 不引用外部條件 -- 兩個(gè)數(shù)據(jù)源的笛卡爾乘積
使用 cross join 關(guān)聯(lián)2個(gè)表, 沒(méi)有關(guān)聯(lián)條件

var query = from b in context.Set<Blog>()
                  from p in context.Set<Post>()
                  select new { b, p }

b. 引用 where 子句中的外部
下面將使用 inner join 關(guān)聯(lián)2個(gè)表

var query = from b in context.Set<Blog>()
            from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId)
            select new { b, p };

下面使用了 DeaultIfEmpty(), 將使用 left join 關(guān)聯(lián)2個(gè)表

var query2 = from b in context.Set<Blog>()
             from p in context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
             select new { b, p };

c. 引用非 where 情況下的外部
使用 Cross Join, 即 Post 無(wú)記錄時(shí), 無(wú)結(jié)果返回;

var query = from b in context.Set<Blog>()
            from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title)
            select new { b, p };

下面使用了 Outer Apply, 也沒(méi)有關(guān)聯(lián)條件,
但是可以支持 left join 類似語(yǔ)法, 即 Post 無(wú)記錄時(shí), 也可能有記錄返回

var query2 = from b in context.Set<Blog>()
             from p in context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
             select new { b, p };
  • GroupBy

GroupBy 運(yùn)算符創(chuàng)建 IGrouping<TKey, TElement> 類型的結(jié)果, 由于任何數(shù)據(jù)庫(kù)結(jié)構(gòu)都無(wú)法表示 IGrouping,
聚合運(yùn)算符應(yīng)用于返回標(biāo)量的每個(gè)組時(shí),該運(yùn)算符可在關(guān)系數(shù)據(jù)庫(kù)中轉(zhuǎn)換為 SQL GROUP BY
SQL GROUP BY 也會(huì)受到限制。 它要求只按標(biāo)量值進(jìn)行分組。
因此, 投影只能包含分組鍵列或?qū)α袘?yīng)用的任何聚合
聚合運(yùn)算符: Avg, Count, LongCount, Min, Max, Sum
注意下面的 group by 和 最后一個(gè)的 select , 只按標(biāo)量值分組, 投影只包含分組健 和 聚合運(yùn)算函數(shù)

var query = from p in context.Set<Post>() group p by p.AuthorId into g
             select new { g.Key,  Count = g.Count() };

分組后的聚合運(yùn)算符 ( 即 into g 中的 g ) 出現(xiàn)在 Where 或 OrderBy(或其他排序方式)LINQ 運(yùn)算符中。
它在 SQL 中將 HAVING 子句用于 where 子句。

var query = from p in context.Set<Post>() group p by p.AuthorId into g
                  where g.Count() > 0   orderby g.Key
                  select new { g.Key,  Count = g.Count() };;
  • LeftJoin

注意, LeftJoin 的寫法,
join 后 Into 到一個(gè)別名1中, 然后 from 別名2 in 別名1.DefaultIfEmpty()

var query = from b in context.Set<Blog>()
                  join p in context.Set<Post>() on b.BlogId equals p.BlogId into grouping
                 from p in grouping.DefaultIfEmpty()
                  select new { b, p };```
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容