今天在一些实验中,我注意到了一件有趣的事情:
var dbContextOptionsBuilder = new DbContextOptionsBuilder<MyContext>();
dbContextOptionsBuilder.UseSqlServer(@"Data Source=LAPTOP-HBBAKRHO\SQLEXPRESS;Initial Catalog=myDb;Integrated Security=True");
var context = new MyContext(dbContextOptionsBuilder.Options);
Stopwatch stopWatch;
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p.Id.Equals(12345));
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
// CLOSE.
context.Dispose();
结果:
如您所见,第一个请求始终会花费更多时间。为什么会这样?
我认为每个请求都需要ORM打开/关闭数据库连接,也许不是,并且EF Core打开连接仅是第一次,并将其用于所有下一个请求,直到DbContext
处理完毕的?
答案 0 :(得分:1)
您每次都是从相同的上下文实例通过相同的参数获取数据,因此第二个和第三个get请求将完全不会发送到SQL Server。 EF拥有自己从数据库中加载的所有实体的一级缓存。这就是为什么第二次和第三次要快得多的原因。如果将第二个查询的参数12345更改为其他任何参数,它将比第一个更快,但不会那么快,因为它将向服务器请求数据。
我建议您阅读以下有关EF执行和性能的主题:
答案 1 :(得分:0)
第一个请求比较慢,因为Entity Framework必须从您的模型创建映射视图。这称为“编译模型”。当您执行第一个查询时,会发生这种情况。
因此,在您的实验中,即使没有返回任何记录,第一个查询也应该很慢。
因此,如果您尝试这样的操作:
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -2); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
第一个查询将比第二个查询慢很多,即使它不返回任何记录。
现在您从上下文中获取另一个实体(假设您的上下文中有一个名为employee的dbset)并针对该dbset执行第一个查询,然后对项目执行查询,您将看到项目查询将运行得更快。
stopWatch = Stopwatch.StartNew();
context.Employees.AsNoTracking().SingleOrDefault(e => e == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -1); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
stopWatch = Stopwatch.StartNew();
context.Projects.AsNoTracking().SingleOrDefault(p => p == -2); // record does not exist
stopWatch.Stop();
Debug.WriteLine($"AsNoTracking().SingleOrDefaultAsync, by ID: {stopWatch.ElapsedMilliseconds}");
解决此问题的一种方法是在启动过程中执行“伪”查询,以使第一个用户执行的查询不会变慢