【问题标题】:Is using LinqToSql in a recursive method very bad for performance在递归方法中使用 LinqToSql 对性能非常不利
【发布时间】:2011-01-27 10:11:42
【问题描述】:

我有以下方法使用 LinqToSql 获取节点的所有父节点,但我不知道这对性能有多大影响。

来自节点表:

public partial class Node   
{
    public List<Node> GetAllParents(IEnumerable<Node> records)
    {
        if (this.ParentID == 0)
        {
            // Reach the parent, so create the instance of the collection and brake recursive.
            return new List<Node>();
        }

        var parent = records.First(p => p.ID == ParentID);

        // create a collection from one item to concat it with the all parents.
        IEnumerable<Node> lst = new Node[] { parent };

        lst = lst.Concat(parent.GetAllParents(records));

        return lst.ToList();
    }
}

这样好吗!!或任何改进它的想法!

谢谢。

【问题讨论】:

  • @Ani:不,它在由 LinqToSQL 创建的生成的部分类中。
  • 你不应该担心性能,除非你发现它在执行时确实需要太多时间。这时候你可以看看你最常松懈的地方。

标签: c# linq performance linq-to-sql


【解决方案1】:

因此,上面的代码是向上(父母的)方向走父子层次结构。所以在最坏的情况下,它会导致对数据库进行 n 次查询以获得 n 的层次结构深度。我建议您通过稍微改变方法来尝试延迟执行,例如

public IEnumerable<Node> GetAllParents(IEnumerable<Node> records)
{
        if (this.ParentID == 0)
        {
            // Reach the parent, so create the instance of the collection and brake recursive.
            return new List<Node>();
        }

        var parent = records.Where(p => p.ID == ParentID);
        var parents = parent.Concat(parent.GetAllParents(records));

        return parent;
}

我不是 100% 确定它是否会起作用,但我的想法是利用表达式树/延迟执行,以便在单个数据库行程中触发多个查询。

另一个想法是编写一个存储的过程/视图,它将返回所有父级(查看 sql server 中的 CTE 相同)。

编辑:在上述代码中使用 Where 而不是 First 来查找父级,因为 First 肯定会立即被评估 - (警告:仍然未经测试的代码)

【讨论】:

  • 我的方法会在数据库上进行多次往返吗?我不确定,但我认为不,因为我在调用它时将所有记录传递给该方法,并且它必须在开始递归之前获取所有记录,不是吗?或者我只能使用List&lt;Node&gt; 而不是IEnumerable&lt;Node&gt;
  • @John-89,这里假设输入没有被评估——所以在这种情况下,调用First 方法将强制数据库查询获取请求的父记录。 ToList 还会强制数据库查询获取实际对象。您可以将所有记录 (List&lt;Node&gt;) 传递给您的方法,在这种情况下,将不会有数据库查询,但性能将取决于记录数。例如,假设我们想要一个拥有 100000 名员工的大型组织中的某个员工的组织报告层次结构(10-15 级),您的方法将需要获取所有 100000 名员工,而不是选择 10-15 名。
【解决方案2】:

这将导致对每个父节点的查询。

解决此问题的最佳方法也是使用 CTE 编写存储过程,或者如果上述不可能,则进行广度优先搜索/查询。后者需要对每个级别进行查询,但总体上会导致更少的查询。

【讨论】:

    【解决方案3】:

    我不确定您能做的事情要少得多,这是 - 出乎我的意料 - 相同但不是递归的,因此可能更有效 - 但问题始终是查询父母。

    List<Node> parentList = new List<Node>();
    Node current = this;
    while (current.ParentID != 0)
    {
        // current = this.Parent;
        current = records.First(r => r.ID == current.ParentID);
        parentList.Add(current)
    }
    
    return parentList;
    

    【讨论】:

      【解决方案4】:

      这取决于您的层次结构可能有多大。如果您知道永远不需要递归多次不是问题,但是将整个表加载到内存中而不是向数据库发送多个调用可能会更快。

      【讨论】:

        猜你喜欢
        • 2014-06-25
        • 2017-02-28
        • 2016-06-05
        • 2018-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多