2013-10-02 49 views
3

我有一个模型,第一,实体框架的设计是这样的(4.4版本)实体框架 - 参考不加载

Entity ER diagram 当我用这样的代码加载:

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

快照具有加载了一切(即SnapshotPart,Quote,QuoteType),但DataInfo除外。现在查看SQL,这似乎是因为Quote 0没有FK到DataInfo,因为0..1的关系。 但是,我预料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会自动加载参考?

+1

尝试'PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.FirstOrDefault(pss => pss.Id == snapshotId).Include(“ComponentQuotes.InstrumentQuote.DataInfo”);'你也可以发布失败的代码吗? –

+0

@NicolásStraubValdivieso谢谢!那样做了。对Synax的小修正是PriceSnapshotSummary snapshot = db.PriceSnapshotSummaries.Include(“ComponentQuotes.InstrumentQuote.DataInfo”)。FirstOrDefault(pss => pss.Id == snapshotId);对于有兴趣的其他人。 –

+0

因此,如果没有FK存在,那么在EF中永远不会加载en实体? –

回答

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 implements IEnumerable并为表达式和提供者添加信息。这基本上允许它针对数据库构建和执行sql语句,而不必在获取数据和遍历它们时像载入IEnumerable一样加载整个表。

由于linq推迟表达式的执行直到它被使用,它将IQueryable表达式编译为SQL,并在需要之前执行数据库查询。这可以加快速度,并且每执行一次Where()Select()时都不需要访问数据库,就可以进行表达式链接。副作用是如果对象在db的作用域之外使用,那么在db被处置之后执行sql语句。

要强制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执行对数据库的表达,并得到所有的地址与数千余更大。如果你需要访问地址表中的一个字段,这是非常好的,但是因为我们想要得到一个城市的名字(与你的关系类似的1..1),所以我们会在它能够运行之前再次遇到碰撞:延迟加载。

实体框架lazy loads实体默认情况下,所以没有任何东西从数据库中提取,直到需要。同样,这会大大加快速度,因为如果没有它,每次对数据库的调用都可能将整个数据库带入内存;但具有取决于可用上下文的问题。

您可以将EF设置为eager load(在您的模型中,转到属性并将'Lazy Loading Enabled'设置为False),但这会带来很多您可能不会使用的信息。

针对此问题最好的解决方法是执行里面的东西分贝的范围:

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)); 
} 

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

这给我们留下了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

我证实了数据库上下文仍然是打开的,所以这似乎不是原因。但绝对有这个好消息。同样在分析数据库表时,这个0..1关系意味着在该方向上没有创建FK。所以你的第一个评论解决了我的问题 - 我怀疑这只是EF方面的一些优化? –

+0

你介意分享代码吗?我真的很想知道为什么模型首先不能在数据库上下文仍然打开的情况下工作 –

+0

是的,我会去掉不必要的位并尽快分享。谢谢! –