我对IsLoaded的理解是正确的吗?

本文关键字:IsLoaded 我对 | 更新日期: 2023-09-27 18:17:55

下面是一些代码以及我在LINQPad中进行的假设。谁能确认这就是延迟加载是如何工作的,也许提供任何额外的见解/链接,这样我就可以理解它是如何在后端工作的?提前感谢!

// Step 1.
var record = context.MyTable.First();
// Step 2.
var foreignKey = ForeignKeyTable.Where(x => x.Id == record.ForeignKeyId).Single();
// Step 3.
var entry = context.Entry(record);
// Step 4.
trace(entry.Reference(x => x.ForeignKey).IsLoaded);
// Step 5.
trace(record.ForeignKey.SomeProperty);
  1. 检索一些记录(查询DB)
  2. 检索一个记录,碰巧是record的外键属性,而不使用延迟加载,如record.ForeignKey检索它(DB被查询)。
  3. 获取record实体详细信息
  4. 这是我不确定的部分。在我的测试中,它输出true。我猜IsLoaded不知道record.ForeignKey目前是否有一个值,但知道record.ForeignKey已经在基于它对record.ForeignKeyId的了解和已经建立的关系的上下文中被跟踪。
  5. db似乎没有在这里被击中,我认为这是出于同样的原因IsLoaded在4中返回true。它知道它已经在跟踪foreignKey对象,所以它知道它不需要做延迟加载。

编辑:我想解决的实际问题可以这样说明:

var record = context.MyTable.First();
var foreignKey = new ForeignKey() { Id = record.ForeignKeyId, SomeProperty = 5 };
context.ForeignKeyTable.Attach(foreignKey);
var entry = context.Entry(record);
// Returns false.
trace(entry.Reference(x => x.ForeignKey).IsLoaded);
// Doesn't query for ForeignKey, so it must know it's `loaded` somehow, and
// gets SomeProperty from my new foreignKey object. What???
trace(record.ForeignKey.SomeProperty);

我对IsLoaded的理解是正确的吗?

当您从数据库加载实体或将其附加到上下文时,EF根据主键和外键值自动修复关系(导航属性)。

在这两个代码片段中,您都加载了record,它具有ForeignKeyTable的外键。上下文知道这个值。(如果您在模型中暴露了外键,这并不重要。它将始终被加载,在你的模型中也没有FK属性。您可以在查看SQL查询时看到这一点。)

在这两种情况下,你随后附加一个ForeignKey实体到上下文,它的主键是上下文已经知道的record.ForeignKeyId的值。因此EF会将导航属性record.ForeignKey设置为附加的ForeignKey实体。

显然IsLoaded没有告诉你实体是否附加到上下文,因为在两个例子中它都是附加的,但一个返回true,另一个返回false。它也不会告诉你record.ForeignKeyId是否指的是一个实体,因为在这两个例子中也是如此。

它显然只告诉你实体已经真正从数据库加载(而不仅仅是手动附加)(这也是Intellisense关于IsLoaded的说明)。这是第一个和第二个例子的唯一区别。

似乎延迟加载不仅仅是由IsLoaded标志控制的。如果你为导航属性附加一个实体到上下文,延迟加载就不会再发生了,尽管IsLoadedfalse

如果第二个代码片段中的最后一行实际上会触发延迟加载,会发生什么?被加载的ForeignKey对象必须与您已经附加的ForeignKey对象具有相同的键(因为record将此值作为FK属性ForeignKeyId)。但是因为没有两个具有相同键的对象可以附加到上下文,所以它必须是相同的对象。但是没有必要加载它,因为这样的对象已经在内存中并附加了

// Step 1.
var record = context.MyTable.First();
// Step 2.
var foreignKey = ForeignKeyTable.Where(x => x.Id == record.ForeignKeyId).Single();
// Step 3.
var entry = context.Entry(record);
// Step 4.
trace(entry.Reference(x => x.ForeignKey).IsLoaded);
// Step 5.
trace(record.ForeignKey.SomeProperty);
  1. 检索一些记录(查询DB)。是,结果记录被附加到DbContext。
  2. 检索恰好是record的外键属性的记录,而不使用像record这样的惰性加载。ForeignKey来检索它(查询DB)。是的。如果您想在#1中快速加载外键,您应该使用context.MyTable。Include(m => m. foreignkey).First();这将在1个查询中检索记录和fk。
  3. 获取记录实体的详细信息。的……它是与DbContext相关的实体的详细信息(附加/删除/加载/等)
  4. 这是我不确定的部分。在我的测试中,它输出true。我猜IsLoaded不知道是否记录。ForeignKey当前有一个值,但是知道这个记录。ForeignKey已经根据它的记录知识在上下文中被跟踪了。ForeignKeyId和已经建立的关系。这意味着DbContext不需要运行另一个查询来加载外键的数据。如果执行record。. .
  5. db似乎没有被击中在这里,我认为这是出于同样的原因IsLoaded返回true在4。它知道它已经在跟踪foreignKey对象,所以它知道它不必执行延迟加载。该实体已经在步骤#2中加载,因此不需要从数据库中获取它。

问题编辑后更新

根据EF, IDbSet上的.Attach方法:

将给定实体附加到集的上下文。也就是说,实体以Unchanged状态放置到上下文中,就好像它是从数据库中读取的一样。