2014-01-11 164 views
4

对不起,这是这么长时间,但至少我觉得我得到了所有信息,以能够理解,也许帮助?实体框架 - 伊格装载两个多到许多关系

我想用预先加载从我的数据库加载数据。

的数据被设置在五个表,建立的米两个级别:n的关系。因此,有包含数据的三个表(在层次结构从上到下的方式排列):

CREATE TABLE [dbo].[relations](
    [relation_id] [bigint] NOT NULL 
) 

CREATE TABLE [dbo].[ways](
    [way_id] [bigint] NOT NULL 
) 

CREATE TABLE [dbo].[nodes](
    [node_id] [bigint] NOT NULL, 
    [latitude] [int] NOT NULL, 
    [longitude] [int] NOT NULL 
) 

前两个真的只能由自己身份的ID(勾其他数据在这里不相关进入)。

在这三个数据表之间是两个M:N表,用分选的提示:

CREATE TABLE [dbo].[relations_ways](
    [relation_id] [bigint] NOT NULL, 
    [way_id] [bigint] NOT NULL, 
    [sequence_id] [smallint] NOT NULL 
) 

CREATE TABLE [dbo].[ways_nodes](
    [way_id] [bigint] NOT NULL, 
    [node_id] [bigint] NOT NULL, 
    [sequence_id] [smallint] NOT NULL 
) 

这本质,OpenStreetMap的数据结构的一部分。我让实体框架从这个数据库构建它的对象,并且它按照表格设置类。 m:n表确实存在类。 (我在EF明白,你可以建立你的对象M:N不具有明确的在中间阶级关系 - 我应该尝试改变对象模型这样?)




我想要做什么:我的切入点恰好是关系的一个项目。

我认为这将是最好先急于负载中间M:N的关系,然后在一个循环迭代了这一点,急于负荷最低的国家之一。我尝试这样做,在加载关系按以下方式

IQueryable<relation> query = context.relations; 
query = query.Where(...); // filters down to exactly one 
query = query.Include(r => r.relation_members); 
relation rel = query.SingleOrDefault(); 

和所有它的1:N在短短的一个访问数据库信息 - 好,好。但我注意到它只加载1:n表,而不是中途数据表的“方式”。

如果我修改,像这样的线这并没有改变:

query = query.Include(r => r.relation_members.Select(rm => rm.way)); 

所以我不能在这里装载的中等水平,似乎?

我不能让所有的工作是急切地加载数据的节点级别。我试过如下:

foreach (relation_member rm in rel.relation_members) { 
    IQueryable<way_node> query = rm.way.way_nodes.AsQueryable(); 
    query = query.Include(wn => wn.node); 
    query.Load(); 
} 

这并不工作,并急切地加载中等水平的方式,所有1:每个迭代n在一个声明中way_node的信息,但从节点的信息(纬度/经度) 。如果我访问这些值中的一个,则会触发另一次到数据库的加载,以加载单个节点对象。

这最后一趟是致命的,因为我想加载1间的关系 - > 300点的方法,每个方法 - > 2000个节点。所以最后我打到了服务器1 + 300 + 300 * 2000 ......提升空间,我想。

但是如何?我无法获得用有效语法和急切加载写的最后一条语句。 出于兴趣;有没有办法在一次旅程中加载整个对象图,从一个关系开始?

回答

5

在加载一个往返整个图将是:

IQueryable<relation> query = context.relations; 
query = query.Where(...); // filters down to exactly one 
query = query.Include(r => r.relation_members 
    .Select(rm => rm.way.way_nodes 
     .Select(wn => wn.node))); 
relation rel = query.SingleOrDefault(); 

不过,既然你说,Include高达...Select(rm => rm.way)没有工作,这是不可能的,这将正常工作。 (如果可行,由于生成的SQL的复杂性以及此查询返回的数据和实体的复杂性,性能可能并不好笑)。

您应该进一步调查的第一件事是为什么.Include(r => r.relation_members.Select(rm => rm.way)) doesn'因为它看起来是正确的。你的模型和映射到数据库是否正确?

环路获得通过明确加载的节点应该是这样的:

foreach (relation_member rm in rel.relation_members) { 
    context.Entry(rm).Reference(r => r.way).Query() 
     .Include(w => w.way_nodes.Select(wn => wn.node)) 
     .Load(); 
} 
+0

这两个解决方案,满载以及迭代部分,如此处所示的完美工作,无需更改。非常感谢。我现在有三个新的属性需要学习:.Entry .Reference .Query
有趣的是,满载的速度比分步快100倍。通过网络移动大量冗余数据确实会大大减少为小型结果集发布大量新的小型查询。也许如果第二个查询准备保存重新编译? – Ralf

+0

关于渴望加载实体的方式,我无法让它工作。我认为所有的映射都可以,他们是从数据库自动生成的,我没有改变它们。它可能是因为实体只包含它自己的ID而没有其他字段而没有加载?当关系对方被加载时,id是已知的,所以在我已经知道的数字中加入另一个表没有多大意义?!并不重要 - 所有的作品都是我喜欢的。 – Ralf

+0

@Ralf:如果您使用EF 5和.NET 4.5,查询只编译一次,然后由EF自动缓存。因此EF不必重新编译循环中的查询。但对于.NET 4.0,查询不会被缓存。关于仅具有ID属性的实体:那么,如果EF“认为”它不是必需的,因为它不包含附加信息,那将会很有趣。我不认为这是为什么渴望加载不起作用的原因,但谁知道...... :) – Slauma

1

Include()出于某种原因有时候会忽略当有排序/分组接合参与/。

在大多数情况下,你可以重写一个包括()作为Select()成一个匿名中介对象:

前:

context.Invoices 
    .Include(invoice => invoice .Positions) 
    .ToList(); 

后:

context.Invoices 
    .Select(invoice => new {invoice, invoice.Positions}) 
    .AsEnumerable() 
    .Select(x => x.invoice) 
    .ToList(); 

这查询方式永远不应该丢失Include()信息。