(24)ASP.NET Core EF查询(查询的工做原理、跟踪与非跟踪查询)

1.查询生命周期

在进入正题时候,咱们先来了解EF Core查询的生命周期。数据库

1.1LINQ查询会由Entity Framework Core处理并生成给数据库提供程序可处理的表示形式(说白了就是生成给数据库可识别数据形式)。

●发送的查询结果(查询表示形式)会被缓存,以便每次执行查询时无需进行1.1中处理。缓存

1.2查询结果(查询表示形式)会传递到数据库提供程序

●数据库提供程序会识别出查询的哪些部分能够在数据库中求值。
●查询的这些部分会转换为特定数据库的查询语言(例如,关系数据库的T-SQL)。
●一个或多个查询会发送到数据库并返回结果集(返回的是数据库中的值,而不是实体实例中的)。测试

1.3对于结果集中的每一项

1.3.1若是这是跟踪查询(后续会讲到),EF会检查数据是否表示已在上下文实例的更改跟踪器中的实体中。

●若是是,则会返回现有实体。
●若是不是,则会建立新实体、设置更改跟踪并返回该新实体。url

1.3.2若是这是非跟踪查询(后续会讲到),EF会检查数据是否表示已在此查询的结果集中的实体中。

●若是是,则会返回现有实体。非跟踪查询使用弱引用跟踪已返回的实体。若是具备相同标识的上一个结果超出范围,并运行垃圾回收,则可能会得到新的实体实例。
●若是不是,则会建立新实体并返回该新实体。spa

1.4执行查询时

当调用LINQ运算符时,只会生成查询的内存中表示形式。当咱们使用查询结果(查询表示形式)时才会发送到数据库。致使查询发送到数据库的最多见操做以下:
●在for循环中循环访问结果:3d

var blogs = from b in _context.Blog
         select new
         {
            b.BlogId,
            b.Url
         };
//触发数据库查询
foreach(var blog in blogs)
{
    var id = blog.BlogId;
}

当咱们执行完LINQ运算符的时候,从SQL Server Profiler监控里面能够看到,并无执行的SQL语句,也就是说查询结果blogs并无当即发送给数据库获取返回数据结果集。
调试

而当咱们调试进去for循环时候,SQL Server Profiler监控里面能够看到出现了执行SQL语句。也就是说这时候查询结果blogs才执行发送给数据库返回结果集。
code

●使用ToList、ToArray、Single、Count等运算符blog

_context.Blog.ToList();
_context.Blog.ToArray();
_context.Blog.Count();
_context.Blog.Single();
_context.Blog.First();

执行这种形式运算符也会当即发送到数据库获取结果集的。具体执行过程呈现,这里大伙自行测试吧。
●将查询结果数据绑定到UI生命周期

2.跟踪查询与非跟踪查询

在1小节生命周期里面咱们有说起过跟踪与非跟踪查询,如今咱们来了解下这两种查询区别。

2.1跟踪查询

返回实体类型的查询是默认会被跟踪的,这表示若是这些实体实例有更改行为,会经过SaveChanges()持久化将更改的值更新到数据库中,可是若是更改的值跟实体实例的值相同,则不会持久化提交数据到数据库,这就是跟踪查询。在如下示例中,将检测到对博客连接所作的更改,并在 SaveChanges() 期间将这些更改持久化到数据库中。

//返回blog实体类型的查询是默认会被跟踪
var blog = _context.Blog.SingleOrDefault(b => b.BlogId == 1);
//检测对博客连接所作的更改
blog.Url = "1";
//持久化保存到数据库中
_context.SaveChanges();

实体初始连接值是1,当咱们点击Save按钮保存的时候,检测到对博客连接所作的更改值仍是1的时候,并不会提交更改值到数据库中的。看看下图SQL Server Profiler监控就知道:

当咱们再把连接值更改成2点击保存时候,EF Core检测到博客连接值已经从1更改成2,就会持久化保存到数据库中。

blog.Url = "2";

废话少说,直接上图:

 

 

2.2非跟踪查询

若是不须要更新从数据库中检索到的实体,则应使用非跟踪查询。能够将单个查询替换为非跟踪查询。

var blogs = context.Blogs
//不用跟踪查询
    .AsNoTracking()
    .ToList();
//或者在上下文实例级别更改默认跟踪行为
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();

仍是同样老谭秘方事例,当你加上非跟踪查询标识后,不管怎么更改博客连接值,都不会持久化保存数据到数据库中的。

var blogs = _context.Blog
//不用跟踪查询
.AsNoTracking()
.SingleOrDefault(m => m.BlogId == 1);
blogs.Url = "2";
_context.SaveChanges();

直接上图跟踪结果:

在这相信你们从该小节跟踪与非跟踪查询中事例描述中总算对1小节查询生命周期有必定理解吧。

2.3跟踪和自定义投影

即便查询的结果类型不是实体类型,默认状况下EF Core也会跟踪结果中包含的实体类型。在如下返回匿名类型的查询中,结果集中的Blog实例会被跟踪。

var blog = context.Blogs
    .Select(b =>
        new
        {
            Blog = b,
            PostCount = b.Posts.Count()
        });

若是结果集包含来自LINQ组合的实体类型,EF Core将跟踪它们。

var blog = context.Blogs
    .Select(b =>
        new
        {
            Blog = b,
            Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault()
        });

若是结果集不包含任何实体类型,则不会执行跟踪。在如下查询中,咱们返回匿名类型(具备实体中的某些值,但没有实际实体类型的实例)。查询中没有任何被跟踪的实体。

var blog = context.Blogs
    .Select(b =>
        new
        {
            Id = b.BlogId,
            Url = b.Url
        });

EF Core支持执行顶级投影中的客户端评估。若是EF Core具体化实体实例以进行客户端评估,则会跟踪该实体实例。此处,因为咱们要将blog实体传递到客户端方法StandardizeURL,所以EF Core也会跟踪博客实例。

var blogs = context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(blog => new
    {
        Id = blog.BlogId,
        Url = StandardizeUrl(blog)
    })
    .ToList();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();
    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }
    return url;
}

EF Core不会跟踪结果中包含的无键实体实例。但EF Core会根据上述规则跟踪带有键的实体类型的全部其余实例。

参考文献:
查询的工做原理
跟踪与非跟踪查询

相关文章
相关标签/搜索