【问题标题】:Entity Framework - Reference not loading实体框架 - 参考未加载
【发布时间】:2013-10-05 03:08:33
【问题描述】:

我有这样一个模型优先的实体框架设计(4.4 版)

当我使用这样的代码加载它时:

PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.FirstOrDefault(pss => pss.Id == snapshotId);

快照已加载除 DataInfo 之外的所有内容(即 SnapshotPart、Quote、QuoteType)。现在查看 SQL,这似乎是因为 Quote 由于 0..1 关系而对 DataInfo 没有 FK。 但是,我原以为 Quote 上的导航属性“DataInfo”仍会转到数据库中来获取它。

我目前的工作是这样的:

foreach (var quote in snapshot.ComponentQuotes)
{
    var dataInfo = db.DataInfoes.FirstOrDefault(di => di.Quote.Id == quote.InstrumentQuote.Id);
    quote.InstrumentQuote.DataInfo = dataInfo;
}

有没有更好的方法来实现这一点?我以为 EF 会自动加载引用?

【问题讨论】:

  • 试试PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.FirstOrDefault(pss => pss.Id == snapshotId).Include("ComponentQuotes.InstrumentQuote.DataInfo"); 你也能贴出失败的代码吗?
  • @NicolásStraubValdivieso 谢谢!做到了。对 synax 的较小修正是 PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.Include("ComponentQuotes.InstrumentQuote.DataInfo").FirstOrDefault(pss => pss.Id == snapshotId);对于其他感兴趣的人。
  • 所以在EF中总是这样,如果不存在FK,它不会默认加载实体?
  • 如果你包含使用System.Data.Entity,你可以在.Include()中使用lambda表达式。
  • 很好,不知道。

标签: c# entity-framework ef-model-first


【解决方案1】:

这个问题与基本 linq 构建块如何与实体框架交互有关。

获取以下(伪)代码:

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000);
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

这看起来不错,但会抛出运行时错误,因为有一个名为 IQueryable 的接口。

IQueryable 实现 IEnumerable 并为表达式和提供者添加信息。这基本上允许它针对数据库构建和执行 sql 语句,而不必像在 IEnumerable 上那样在获取数据和迭代它们时加载整个表。

由于 linq 将表达式的执行推迟到使用之前,它会将 IQueryable 表达式编译为 SQL,并仅在需要之前执行数据库查询。这大大加快了速度,并允许表达式链接,而无需每次执行 Where()Select() 时访问数据库。副作用是如果对象在db作用域之外使用,那么sql语句在db被释放后执行。

要强制 linq 执行,可以使用 ToList,如下所示:

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000).ToList();
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

这将强制 linq 对 db 执行表达式并获取所有大于一千的地址。如果您需要访问地址表中的字段,这一切都很好,但是由于我们想要获取城市的名称(类似于您的 1..1 关系),我们将在它运行之前遇到另一个问题:延迟加载。

默认情况下实体框架lazy loads 实体,因此在需要之前不会从数据库中获取任何内容。同样,这大大加快了速度,因为没有它,每次调用数据库都可能将整个数据库带入内存;但存在依赖可用上下文的问题。

您可以将 EF 设置为 eager load(在您的模型中,转到属性并将“启用延迟加载”设置为 False),但这会带来很多您可能不会使用的信息。

解决此问题的最佳方法是执行 db 范围内的所有内容:

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Where(addr => addr.Number > 1000);
    addresses.Select(addr => Console.WriteLine(addr.City.Name));
}

我知道这是一个非常简单的示例,但在现实世界中,您可以使用像 ninject 这样的 DI 容器来处理您的依赖项,并在应用程序的整个执行过程中让您的数据库可供您使用。

这给我们留下了Include。 Include 会让 IQueryable 在构建 sql 语句时包含所有指定的关系路径:

IQueryable<Address> addresses;
Using (var db = new ObjectContext()) {
    addresses = db.Users.Addresses.Include("City").Where(addr => addr.Number > 1000).ToList;
}

addresses.Select(addr => Console.WriteLine(addr.City.Name));

这会起作用,它是必须加载整个数据库和必须重构整个项目以支持 DI 之间的一个很好的折衷。

您可以做的另一件事是映射multiple tables to a single entity。在您的情况下,由于关系是 1-0..1,因此您应该没有问题。

【讨论】:

  • 我验证了数据库上下文仍然是打开的,所以这似乎不是原因。但这绝对是很好的了解未来。同样在分析数据库表时,这种 0..1 关系意味着没有在该方向上创建 FK。所以你的第一条评论解决了我的问题 - 我怀疑这只是 EF 方面的一些优化?
  • 您介意分享代码吗?我真的很想知道为什么 model first 不能使用 db context 仍然打开
  • 是的,我会去掉不必要的部分并尽快分享。谢谢!
猜你喜欢
  • 2022-01-16
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多