2020-05-14 - Linq 查询

  • Entity Framework Core 使用语言集成查询 (LINQ) 来查询数据库中的数据。

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

  • 客户端与服务器端评估

    • Entity Framework Core 会尝试尽可能在服务器上查询。
    • EF Core 支持在顶级投影中进行部分客户端评估(基本上为最后一次调用 Select())。 如果查询中的顶级投影无法转换为服务器,EF Core 将从服务器中提取任何所需的数据,并在客户端上评估查询的其余部分。

      指最后一次Select时, 如果遇到服务器不认识的转换,则将可以在客户端进行转换
      * EF Core 在顶级投影之外的任何位置检测到不能转换为服务器的表达式,则会引发运行时异常。
      > 即在最后一次 Select 之前,如果遇到不能转换的表达式,则抛出异常

    • 顶级投影中的客户端评估
      var blogs = context.Blogs .Select(blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) }) .ToList();
      上面代码中, 最后一个Select中的StandardizeUrl 函数不被服务器认识,所以将先在服务器查询所有数据,然后在客户端转换
    • 不支持的客户端评估
      var blogs = context.Blogs .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) .ToList();
      上面代码中, ToList为最后一次 Select, 而之前的 where 中包含不被服务器认识的函数 StandardizeUrl, 所以将抛出异常
    • 显式客户端评估
      a. 由于数据量小,因此在进行客户端评估时才不会大幅减弱性能。
      b. 所用的 LINQ 运算符不会进行任何服务器端转换
      c. 通过调用 AsEnumerable 或 ToList 等方法(若为异步,则调用 AsAsyncEnumerable 或 ToListAsync),以显式方式选择进行客户端评估
      d. AsEnumerable 将对结果进行流式传输, 而 ToList 会占用额外的内存
      var blogs = context.Blogs .AsEnumerable() .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) .ToList();
      上面代码中, AsEnumerable 为最后一次 Select,
    • 内存泄漏
      在 Linq查询中,使用了实例中的常数, 而 Linq查询 会缓存表达式, 这使得实例无法释放, 因为其被缓存所使用;
  • 跟踪与非跟踪查询

    • 非跟踪查询
      var blogs = context.Blogs.AsNoTracking().ToList(); 或者变更整个 DbContext 为不跟踪
      context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    • 标识解析

    EF Core 将在跟踪查询中执行标识解析。 当具体化实体时,如果 EF Core 已被跟踪,则会从更改跟踪器返回相同的实体实例。 如果结果中多次包含相同的实体,则每次会返回相同的实例。

    非跟踪查询不会使用更改跟踪器,也不会执行标识解析。 因此会返回实体的新实例

  • 复杂查询

    • join -- 连接2个表, 有关联比较条件

    将使用 inner join 关联2个表, 这个需要有关联条件

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个表, 无关联条件

a. 不引用外部条件 -- 两个数据源的笛卡尔乘积
使用 cross join 关联2个表, 没有关联条件

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

b. 引用 where 子句中的外部
下面将使用 inner join 关联2个表

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 关联2个表

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 无记录时, 无结果返回;

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, 也没有关联条件,
但是可以支持 left join 类似语法, 即 Post 无记录时, 也可能有记录返回

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 运算符创建 IGrouping<TKey, TElement> 类型的结果, 由于任何数据库结构都无法表示 IGrouping,
聚合运算符应用于返回标量的每个组时,该运算符可在关系数据库中转换为 SQL GROUP BY
SQL GROUP BY 也会受到限制。 它要求只按标量值进行分组。
因此, 投影只能包含分组键列或对列应用的任何聚合
聚合运算符: Avg, Count, LongCount, Min, Max, Sum
注意下面的 group by 和 最后一个的 select , 只按标量值分组, 投影只包含分组健 和 聚合运算函数

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

分组后的聚合运算符 ( 即 into g 中的 g ) 出现在 Where 或 OrderBy(或其他排序方式)LINQ 运算符中。
它在 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 到一个别名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 };```
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352