3

这是一些代码以及我基于在 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. 检索一些记录(查询数据库)。
  2. 检索恰好是外键属性的记录,record而无需使用延迟加载record.ForeignKey来检索它(查询数据库)。
  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);
4

2 回答 2

2

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

在您加载的两个代码片段中record,它们都有一个外键到您的ForeignKeyTable. 上下文知道这个值。(顺便说一句,如果您在模型中公开了外键也没关系。它将始终被加载,而且您的模型中也没有 FK 属性。您可以在观看 SQL 查询时看到这一点。)

在这两种情况下,您之后都将一个实体附加到上下文中,该实体具有上下文已经知道ForeignKey的值作为主键。record.ForeignKeyId因此,EF 会将导航属性record.ForeignKey设置为此附加ForeignKey实体。

显然IsLoaded不会告诉您实体是否附加到上下文,因为在两个示例中它都已附加,但一个返回true,另一个返回false。它也不会告诉您是否record.ForeignKeyId引用了实体,因为在两个示例中也是如此。

它显然只告诉您该实体确实已从数据库中加载(而不仅仅是手动附加)(这也是 Intellisense 所说的IsLoaded)。这是您的第一个和第二个示例之间的唯一区别。

而且似乎延迟加载不仅受IsLoaded标志控制。如果您将导航属性的实体附加到上下文,则不再发生延迟加载,尽管IsLoadedis false.

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

于 2011-12-15T21:30:16.340 回答
1
// 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. 检索一些记录(查询数据库)。是的,结果记录附加到 DbContext。
  2. 检索恰好是记录的外键属性的记录,而不使用像 record.ForeignKey 这样的延迟加载来检索它(查询数据库)。是的。如果您想在 #1 中急切加载外键,您将使用 context.MyTable.Include(m => m.ForeignKey).First(); 这将检索记录以及 fk in 1 查询。
  3. 获取记录实体的详细信息。有点......它是与 DbContext 相关的实体的详细信息(附加/删除/加载/等)
  4. 这是我不确定的部分。在我的测试中,它输出 true。我猜 IsLoaded 不知道 record.ForeignKey 当前是否有值,但知道 record.ForeignKey 已经根据它对 record.ForeignKeyId 的了解和已建立的关系在上下文中被跟踪。这意味着 DbContext 不需要运行另一个查询来加载外键的数据。如果执行record.ForeignKey,数据已经存在,不需要额外去db
  5. db 似乎没有在这里被击中,我认为这与 IsLoaded 在 4 中返回 true 的原因相同。它知道它已经在跟踪 foreignKey 对象,所以它知道它不必进行延迟加载。该实体已在步骤 #2 中加载,因此无需额外的行程即可从数据库中获取它。

问题编辑后更新

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

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

于 2011-12-15T20:05:43.220 回答