【问题标题】:Entity Framework Slow Filtering Associated Entities实体框架慢过滤关联实体
【发布时间】:2014-04-03 08:13:37
【问题描述】:

在实体上填充过滤的子 EntityCollection 时,EntityFramework 似乎非常慢 -

我们正在运行 RIA/WCF Web 应用程序,该应用程序使用以 C# 编写的数据库优先实体框架和 Silverlight 客户端。我们收集了大约 70,000 个“Eval”实体和类似数量的“InCo”实体。每个 Eval 都有一个包含最多 2 个 InCo 实体的子集合。这种关系是一对多的,因此一个 Eval 可以有多个 InCo,但一个 InCo 绑定到一个 Eval。从数据库加载实体集合后,我像这样循环遍历 Eval -

    foreach(Eval eval in Evals)
        if (eval.InCos.Count > 0)
            // Do something

这需要很长时间(分钟)。单步执行代码我发现在 Eval 实体上有一个生成的函数 FilterInCos 来确定 InCo 是否与它有关联(在生成的 .Web.g.cs 文件中)并将其传递给Eval 的 InCos EntityCollection 的构造函数,并在第一次引用其属性之一时调用以填充该集合

    private bool FilterInCos(InCo entity)
    {
        return (entity.EvalID == this.EvalID);
    }

据我所见,每个 Eval 的 InCos 集合中的每个 InCo 都会调用 FilterInCos() 函数 - 即 70,000 个 Eval 中的每一个都调用了 70,000 次,导致大约 50 亿次迭代循环遍历 Eval 并查询 InCos收集到每一个。由于 Eval:InCo 数据库关系为 1:*,因此应该可以遍历 InCo 对象,检索具有匹配 EvalID 的 Eval 并将 InCo 添加到该 Eval 的 InCos 集合中 - 70,000 次迭代。但是,正如我所见,似乎没有办法绕过生成的逻辑。我们也不能只将 EntityCollection 分配给 Eval 的 InCos 属性,因为它是只读的。

这是来自 EF edmx 文件的相关数据库关系 -

    <Association Name="FK_InCo_Eval">
      <End Role="Eval" Type="AspireEntityModel.Store.Eval" Multiplicity="1">
        <OnDelete Action="Cascade" />
      </End>
      <End Role="InCo" Type="AspireEntityModel.Store.InCo" Multiplicity="*" />
      <ReferentialConstraint>
        <Principal Role="Eval">
          <PropertyRef Name="EvalID" />
        </Principal>
        <Dependent Role="InCo">
          <PropertyRef Name="EvalID" />
        </Dependent>
      </ReferentialConstraint>
    </Association>

这是带有生成构造函数的 Eval 客户端实体(来自生成的 .Web.g.cs)上的 InCos 属性

    /// <summary>
    /// Gets the collection of associated <see cref="InCo"/> entity instances.
    /// </summary>
    [Association("Eval_InCo", "EvalID", "EvalID")]
    [XmlIgnore()]
    public EntityCollection<InCo> InCos
    {
        get
        {
            if ((this._InCos == null))
            {
                this._InCos = new EntityCollection<InCo>(this, "InCos", this.FilterInCos, this.AttachInCos, this.DetachInCos);
            }
            return this._InCos;
        }
    }

是否有人在数据库优先的 EF 应用程序上发现了类似的行为并可以提出解决方法?

【问题讨论】:

    标签: c# entity-framework ria


    【解决方案1】:

    当生产数据库开始获得数百万条记录时,我发现了一个非常相似的问题。长话短说:-

    首先)除非我正在编写查询,否则我总是使用 .ToList() 在任何 foreach() 循环之前强制对数据库进行一次往返。

    第二)如果我可以在第一个查询中收集数据,我从不在循环中使用导航属性。

    第三)我尝试获取两个单独的列表(使用 .ToList )一个 Eval 和一个 InCos ,而不是获得非常广泛的结果,然后执行 Linq.FirstOrDefault() 将它们加入内存,但是我必须考虑内存开销和数据传输时间。

    在您的情况下,我会修改用于收集 Eval 的选择,例如:-

    var Evals = from Ev in db.Evals
                select new {
                    Ev,
                    Cnt = Ev.Incos.Count()
                    }
    
    foreach(var Eval in Evals)
    if (Eval.Cnt > 0)
        .......
    
    and access the Eval as Eval.Ev
    

    【讨论】:

      【解决方案2】:

      首先,您可以通过在查询本身中过滤来提高查询的效率:

      foreach(Eval eval in Evals.Where(e => e.InCos.Any())
      

      这会将所有过滤转移到服务器,因此无需对每个 Eval 记录进行额外的昂贵查询。

      如果你需要对 InCos 做一些事情,你应该急切地加载它们:

      foreach(Eval eval in Evals.Expand("InCos").Where(e => e.InCos.Any())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-14
        • 1970-01-01
        • 1970-01-01
        • 2017-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多