客户端与服务端求值
在下面的示例中,一个辅助方法用于标准化从 SQL Server 数据库中返回的博客的 URL。 由于 SQL Server 提供程序不了解此方法的实现方式,因此不可以将其转换为 SQL。 除了在客户端上是通过执行该方法来返回 URL,查询的其余部分都是在数据库中执行的。
var blogs = context.Blogs .OrderByDescending(blog => blog.Rating) .Select(blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) }) .ToList();
public static string StandardizeUrl(string url) { url = url.ToLower(); if (!url.StartsWith("http://")) { url = string.Concat("http://", url); } return url; }
客户端求值性能问题
var blogs = context.Blogs .Where(blog => StandardizeUrl(blog.Url).Contains("dotnet")) .ToList();
默认情况下,当执行客户端求值时,EF Core 将记录警告,客户端求值引发异常。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer(@"Server= (localdb)mssqllocaldb;Database=EFQuerying;Trusted_Connection=True; ") .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); }
跟踪与非跟踪查询
在 EF Core 中查询数据时,EF Core 内置的跟踪器将对查询返回的实体进行跟踪,当修改实体时,会将检测到的任何更改
都会在 SaveChanges() 期间永久保存到数据库。
EF CORE 跟踪器对一个对象跟踪以下四种状态
Added: 实体在数据库中不存在,调用 SaveChanges 方式时会将实体 Insert 插入到数据库。
Unchanged:实体存在于数据库中,但客户端查询出来后未进行过修改,当调用 SaveChanges 时将忽略。
Updateed:实体存在数据库中,查询出来后进行了修改,当调用 SaveChanges 方法时,将 Update 更新到数据库。
Deleted:实体存在于数据库,单客户端调用了删除方法从内存中移除,用 SaveChanges 方法时,将从数据库中Delete 移除。
跟踪查询
using (var context = new BloggingContext()) { var blog = context.Blogs.SingleOrDefault(b => b.BlogId == 1); blog.Rating = 5; context.SaveChanges(); }
虽然跟踪器非常方便,但当查询数据时,上下文会创建实体的快照,实体从而被跟踪比较变更。跟踪的目的是监控变更, 当提交 SaveChanges时,保存到数据库,但有时候,我们只查询数据,只读数据,不写入,不需要修改数据,这个时
候,我们跟踪器就时多余的了,但跟踪器会占用快照内存和跟踪本身的性能消耗,所以不跟踪最好,可以这样做。
using (var context = new BloggingContext()) { var blogs = context.Blogs .AsNoTracking() .ToList(); }
以上针对某次查询中不跟踪设置,还可全局设置查询不跟踪,这样就方便一次查询多张表,不用每个都 AsNoTracking了。
using (var context = new BloggingContext()) { context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var blogs = context.Blogs.ToList(); }
跟踪与投影
即使查询的结果类型不是实体类型,但如果结果包含实体类型,则默认情况下也会跟踪这些实体类型。 在以下返回匿名类型的查询中,会跟踪结果集中 Blog 的实例。
using (var context = new BloggingContext()) { var blog = context.Blogs .Select(b => new { Blog = b, Posts = b.Posts.Count() }); }
如果结果集不包含任何实体类型,则不会执行跟踪。 在以下返回匿名类型(具有实体中的某些值,但没有实际实体类型的实例)的查询中,不会执行跟踪。
using (var context = new BloggingContext()) { var blog = context.Blogs .Select(b => new { Id = b.BlogId, Url = b.Url }); }