-
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 };```
网友评论