3

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

实体 ER 图 当我使用这样的代码加载它时:

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会自动加载参考?

4

1 回答 1

2

这个问题与基本 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,并仅在需要之前执行数据库查询。这大大加快了速度,并允许表达式链接,而无需每次执行 a Where()or时都进入数据库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 关系),我们将在它运行之前遇到另一个问题:延迟加载。

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

您可以将 EF 设置为预加载(在您的模型中,转到属性并将“启用延迟加载”设置为 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 之间的一个很好的折衷。

您可以做的另一件事是将多个表映射到单个实体。在您的情况下,由于关系是 1-0..1,因此您应该没有问题。

于 2013-10-02T16:17:19.303 回答