【问题标题】:Entity Framework - Eager load two many-to-many relationships实体框架 - 渴望加载两个多对多关系
【发布时间】:2014-01-11 17:01:28
【问题描述】:

抱歉这么长,但至少我认为我得到了所有信息以便能够理解并可能有所帮助?

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

数据设置在五个表中,设置两个级别的 m: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 数据结构的一部分。我让 Entity Framework 从这个数据库构建它的对象,并且它完全按照表的方式设置类。 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();
}

这确实有效,并且在每次迭代的一个语句中急切地加载中间层方式和 way_node 的所有 1:n 信息,但不是来自节点的信息(纬度/经度)。如果我访问其中一个值,我会触发另一次访问数据库以加载单个节点对象。

最后一次旅行是致命的,因为我想加载 1 个关系 -> 300 种方式,每种方式 -> 2000 个节点。所以最后我打了服务器 1 + 300 + 300*2000... 我认为还有改进的余地。

但是怎么做呢?我无法以有效的语法和急切的加载方式编写最后一条语句。 不感兴趣;有没有一种方法可以一次加载整个对象图,从一个关系开始?

【问题讨论】:

    标签: entity-framework linq-to-entities many-to-many eager


    【解决方案1】:

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

    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 =&gt; rm.way) 都不起作用,因此这不太可能起作用。 (如果它能够工作,由于生成的 SQL 的复杂性以及此查询将返回的数据和实体的数量,性能可能并不好。)

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

    通过显式加载获取节点的循环应如下所示:

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

    【讨论】:

    • 两种解决方案,全负载以及迭代部分,都可以完美地工作,如图所示,无需更改。非常感谢。我现在要学习三个新属性: .Entry .Reference .Query
      有趣的是,完全加载比逐步加载快大约 100 倍。在网络上移动大量冗余数据确实比为小结果集发出大量新的小查询要小得多。也许如果第二个查询准备好保存重新编译?
    • 关于急切加载实体的方式我无法让它工作。我认为所有映射都可以,它们是从数据库自动生成的,我没有更改它们。会不会因为实体只包含它自己的 ID 而没有其他字段而没有加载它们?当加载另一方的关系时,id 是已知的,所以为我已经知道的数字加入另一个表没有多大意义?!没关系 - 一切都如我所愿。
    • @Ralf:如果您使用 EF 5 和 .NET 4.5,则查询只编译一次,然后由 EF 自动缓存。因此 EF 不必重新编译循环中的查询。但是对于 .NET 4.0,查询不会被缓存。关于只有 ID 属性的实体:好吧,如果 EF “认为”它不是必需的,那将很有趣,因为它不包含其他信息。我不认为这是急切加载不起作用的原因,但谁知道... :)
    【解决方案2】:

    Include() 有时在涉及排序/分组/加入时会被忽略。

    在大多数情况下,您可以将 Include() 作为 Select() 重写为匿名中间对象:

    之前:

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

    之后:

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

    这样查询就不会丢失 Include() 信息。

    【讨论】:

      【解决方案3】:
      //get an associate book to an author
      var datatable = _dataContext.Authors
                                  .Where(x => authorids.Contains(x.AuthorId))
                                  .SelectMany(x => x.Books)
                                  .Distinct();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-01
        • 1970-01-01
        • 2016-09-18
        • 2015-05-16
        相关资源
        最近更新 更多