ASP.NET Core 使用 Entity Framework 查询数据

本章节讲解 ASP.NET Core 使用 Entity Framework 查询数据的方法,将通过实例代码与解释来详细讲解。

查询生命周期

首先,我们来了解一下 Entity Framework Core 查询的生命周期。

  • LINQ 查询会由 Entity Framework Core 处理并生成给数据库提供程序可处理的表示形式(就是生成给数据库可识别数据形式)。发送的查询结果(查询表示形式)会被缓存,以便每次执行查询时无需进行 LINQ 中处理。

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

    • 数据库提供程序会识别出查询的哪些部分可以在数据库中求值。
    • 查询的这些部分会转换为特定数据库的查询语言(例如,关系数据库的 T-SQL)。
    • 一个或多个查询会发送到数据库并返回结果集(返回的是数据库中的值,而不是实体实例中的)。
  • 如果这是跟踪查询,Entity Framework 会检查数据是否表示已在上下文实例的更改跟踪器中的实体中。

    • 如果是,则会返回现有实体。
    • 如果不是,则会创建新实体、设置更改跟踪并返回该新实体。
  • 如果这是非跟踪查询,Entity Framework 会检查数据是否表示已在此查询的结果集中的实体中。

    • 如果是,则会返回现有实体。非跟踪查询使用弱引用跟踪已返回的实体。如果具有相同标识的上一个结果超出范围,并运行垃圾回收,则可能会获得新的实体实例。
    • 如果不是,则会创建新实体并返回该新实体。
  • 执行查询时,当调用 LINQ 运算符时,只会生成查询的内存中表示形式。当我们使用查询结果(查询表示形式)时才会发送到数据库。导致查询发送到数据库的最常见操作如下:

for 循环中循环访问结果:

  1. var employees = from b in _context.Employees
  2. select new
  3. {
  4. b.EmployeeName,
  5. b.ID
  6. };
  7. //触发数据库查询
  8. foreach (var a in employees)
  9. {
  10. var id = a.ID;
  11. }

当我们执行完 LINQ 运算符的时候,从 SQL Server Profiler 监控里面可以看到,并没有执行的 SQL 语句,也就是说查询结果 employees 并没有立即发送给数据库获取返回数据结果集。

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

另外,使用 ToListToArraySingleCount 等运算符。执行这种形式运算符也会立即发送到数据库获取结果集。

  1. _context.Employees.ToList();
  2. _context.Employees.ToArray();
  3. _context.Employees.Count();
  4. _context.Employees.Single();
  5. _context.Employees.First();

跟踪查询与非跟踪查询

跟踪查询

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

  1. //返回实体类型的查询是默认会被跟踪
  2. var employee = _context.Employees.SingleOrDefault(b => b.ID == 1);
  3. //检测对员工名称所做的更改
  4. employee.EmployeeName = "韩梅梅1";
  5. //持久化保存到数据库中
  6. _context.SaveChanges();
非跟踪查询

如果不需要更新从数据库中检索到的实体,则应使用非跟踪查询。可以将单个查询替换为非跟踪查询。

  1. var employee = context.Employees
  2. //不用跟踪查询
  3. .AsNoTracking()
  4. .ToList();
  5. //或者在上下文实例级别更改默认跟踪行为
  6. context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
  7. var employees = context.Employees.ToList();

当加上非跟踪查询标识后,无论怎么更改值,都不会持久化保存数据到数据库中的。

  1. var employee = _context.Employees
  2. //不用跟踪查询
  3. .AsNoTracking()
  4. .SingleOrDefault(m => m.ID == 1);
  5. employee.EmployeeName = "韩梅梅2";
  6. _context.SaveChanges();
跟踪和自定义投影

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

  1. var employee = context.Employees
  2. .Select(b =>
  3. new
  4. {
  5. Employee = b,
  6. EmployeeCount = b.EmployeeName.Count()
  7. });

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

  1. var employee = context.Employees
  2. .Select(b =>
  3. new
  4. {
  5. Employee = b,
  6. EmployeeLogs = b.EmployeeLog.OrderBy(p => p.EmployeeID).LastOrDefault()
  7. });

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

  1. var employee = context.Employees
  2. .Select(b =>
  3. new
  4. {
  5. Id = b.ID,
  6. Name = b.EmployeeName
  7. });

EF Core 支持执行顶级投影中的客户端评估。如果 EF Core 具体化实体实例以进行客户端评估,则会跟踪该实体实例。此处,由于我们要将 employee 实体传递到客户端方法 StandardizeURL,因此 EF Core 也会跟踪该实例。

分类导航