(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会检查数据是否表示已在上下文实例的更改跟踪器中的实体中。
●如果是,则会返回现有实体。
●如果不是,则会创建新实体、设置更改跟踪并返回该新实体。
1.3.2如果这是非跟踪查询(后续会讲到),ef会检查数据是否表示已在此查询的结果集中的实体中。
●如果是,则会返回现有实体。非跟踪查询使用弱引用跟踪已返回的实体。如果具有相同标识的上一个结果超出范围,并运行垃圾回收,则可能会获得新的实体实例。
●如果不是,则会创建新实体并返回该新实体。
1.4执行查询时
当调用linq运算符时,只会生成查询的内存中表示形式。当我们使用查询结果(查询表示形式)时才会发送到数据库。导致查询发送到数据库的最常见操作如下:
●在for循环中循环访问结果:
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才执行发送给数据库返回结果集。
●使用tolist、toarray、single、count等运算符
_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会根据上述规则跟踪带有键的实体类型的所有其他实例。
参考文献:
上一篇: postgre 笔记
下一篇: linux加入计划任务