IEnumerable 和延迟加载

IEnumerable and Lazy loading

我有以下型号 -

public class Student
{
     public int StudentID { get; set; }
     public string StudentName { get; set; }
     public Nullable<int> StandardId { get; set; }

     public virtual Standard Standard { get; set; }
}  

public class Standard
{      
     public int StandardId { get; set; }
     public string StandardName { get; set; }

     public virtual ICollection<Student> Students { get; set; }
}

关系是->一个标准可以有多个学生。 当使用 IEnumerable 执行以下代码时,它会执行延迟加载,从下面的日志中可以看出

代码-

using (var ctx = new SchoolDBEntities())
{
  ctx.Database.Log = Logger.Log;

  IEnumerable<Student> studList = ctx.Students;
  foreach(Student std in studList)
  {
     Console.WriteLine("Student Name = " + std.StudentName);

     //Loads Student standard for particular Student only (separate SQL query)
     Standard standard = std.Standard;
     Console.WriteLine("Standard Name = " + standard.StandardName);
  }
}

日志-

从日志中可以看出,"Standard" table 数据是在需要的时候取的。 那为什么说IEnumerable不支持Laze Loading而IQueryable支持呢?

当我 运行 使用 IQueryable 代替 IEnumerable 的代码时,它显示相同的日志。 我没有找到任何示例来解释 IEnumerable 如何不支持延迟加载。

这只是我的猜测,如果我错了,我很乐意得到纠正。

据我所知,IQueryable 继承自 IEnumerable。使用数据库上下文时的底层类型应该是 IQueryable - 这也启用了延迟加载。

您始终可以将 IQueryable 转换为 IEnumerable(反之亦然,通过扩展方法)。 IQueryable 的优点是 LL - 您可以对查询执行 operations/filtering 而无需立即执行。这在处理大量数据时非常有用。

Then why it is said that IEnumerable does not support Laze Loading

延迟加载是一个 ORM 概念(Entity Framework 在你的例子中)。
您可以将其视为实现细节。

IEnumerable<T>IQueryable<T> 本身都不支持延迟加载。

IEnumerable<Student> studList = ctx.Students;

不管变量类型(IEnumerable<Student>),引用的对象类型将是DbSet<Student>,实现IQueryable<Student>.

DbSet<T> 使用 EF 基础结构实现对象。延迟加载由代理类型实现,由 EF 对象物化器生成。这不是 IEnumerable<T>IQueryable<T> 功能。

P.S.

如上所述,延迟执行!=延迟加载。

IEnumerable<T>IQueryable<T> 支持延迟执行。对于 IQueryable<T> 这意味着,将执行该查询,并且仅当您开始枚举查询结果时才会具体化对象。

但这不是延迟加载。 Student.Standard 将在您调用 属性 getter 时加载。这个延迟加载。如您所见,它与 IQueryable<T> 无关:您不需要 IQueryable<T> 来调用 属性 getter.

这是关于运行时类型与编译时类型,以及集合与它包含的对象。

当您执行查询时可以分配给一个 IEnumerable<T> 变量,它仍然是一个 IQueryable<T> 下面。

但是,一旦您对其使用 IEnumerable<T> 方法,该集合将不再是可查询的。例如。调用 ToArrayToList.

这样做的主要影响是进一步的操作(例如 Where 用于过滤)将在内存中进行,而不是更改查询。

但是 这与 EF 关联的惰性属性(代码中的 std.Standard)无关在集合中的单个对象上完成,而不是在集合上完成。

所以如果你有

var qry = /* something to create an IQueryable<T> from your DbContent */

foreach (var x in qry) {
  // qry has been enumerated: so SQL has been generated and executed
}

qry = qry.Where(some-condition);

foreach (var x in qry) {
  // qry has been enumerated: SQL with extra condition applied is run
}    

var e = qry.ToList();

// Anything done to e is done in memory, there will be no new
// SQL sent to the DB for the collection/

var f = e.First();
// But, as Standard is a lazy property this will generated a query.
x = e.Standard.SomeProperty;