【问题标题】:Populating a list within a list in C# not using foreach loop. better way?在 C# 中的列表中填充列表,而不使用 foreach 循环。更好的方法?
【发布时间】:2016-09-09 20:48:30
【问题描述】:

我在对象列表 (List-ParentClass) 中有一个对象列表,其中一个对象是嵌套列表 (List-ChildClass)。为了填充 List-ChildClass,我使用了一个 foreach 循环,如下所示。我还嵌套了一个 linq 查询,如下所示。

在这一点上,我遇到了一些性能问题,我觉得有更好的方法来做到这一点,我只是没有找到。

问题:我怎样才能更好/更快地做到这一点?

注意 - 这是一个用 C# 编写的基于 Web 的 .net MVC 应用程序。我使用 EF 回到 SQL 数据库。

public class ParentClass
{
    public int pcid { get; set; }
    public List<ChildClass> ChildClassList { get; set; }
}

public class ChildClass
{
    public int pcid { get; set; }
    public int ccid { get; set; }
}

public class DoWork 
{
    public void ExampleMethodForEach()
    {
        List<ParentClass> ParentClassList = new List<ParentClass>();

        foreach(ParentClass a in ParentClassList)
        {
            a.ChildClassList = EFDatabase2.where(b => b.pcid == a.pcid).select(b => b.ccid).ToList();
        }
    }

    public void ExampleMethodLinq()
    {
        var ParentClassList = (from a in EFDatabase
                               select new ParentClass
                               {
                                   ccid = a.ccid,
                                   pcid = (from b in EFDatabase2
                                           where b.pcid == a.pcid
                                           select b.ccid).ToList()
                                   //something like this were I nest a query
                               }).ToList();
    }
}

【问题讨论】:

  • 如果 database 存在性能问题,不是 listforeach 等,而是 database 责备。最后的查询是什么?查询获取多少条记录?如果是list within list,请尝试尽可能少地调用:你能一次性获取所有数据,然后将它们组织成你想要的结构吗?
  • 我在 foreach 中发现的是它访问数据库的次数可能会导致问题。在我当前的应用程序中,我将数据拉入内存然后运行 ​​foreach 循环。这表现得更好,但似乎有点笨拙。我一直认为我可以在一个大查询中完成。
  • 就记录数量而言,它相对较小,我们返回一千或两条,但每个列表都有多个列表,其中包含 3 - 10 倍以上的记录。拉这个的查询也有点复杂。因此,深入了解我的情况可能并不那么简单。问题:您认为上述方法是解决这种情况的正常/常用方法吗?
  • 所以记录总数(父母和所有孩子)大约是10000-20000。这不是很多。尝试使查询尽可能简单,例如 select b, parent_id from my_table 调用一次并获取数据以创建/填充您的结构。

标签: c# .net entity-framework linq list


【解决方案1】:

使用关系数据库和 LINQ 的最佳方法是使用 joins 来关联数据。在你的情况下,最合适的是group join

var ParentClassList =
    (from p in EFDatabase
     join c in EFDatabase2 on p.pcid equals c.pcid into children
     select new ParentClass
     {
         pcid = p.pcid,
         ChildClassList =
             (from c in children
              select new ChildClass
              {
                  pcid = c.pcid,
                  ccid = c.ccid
              }).ToList()
     }).ToList();

这应该会给你一个很好的快速单一数据库查询。

附:希望您的EFDatabaseEFDatabase2 变量引用同一个数据库中的两个表。

【讨论】:

  • 这个解决方案给了我最好的性能。将其标记为正确答案。
【解决方案2】:

您多次访问数据库。您有 N+1 个问题。

我的建议是先查询所有的父母,但不包括孩子的数据。然后获取您检索到的所有父母的 ID 并将其放入数组中。我们将使用该数组在 SQL 中创建一个 IN 子句。

使用父 ID 数组加载所有子代后,使用 ToLookup 将它们映射到 Lookup,使用父 ID 作为键,并使用 foreach 将子代列表分配给父代。

var parents = EFDatabase2.Parents.Where(...).Select(p => new ParentClass { pcid = p.pcid }).ToList();
var ids = parents.Select(p => p.pcid).ToArray();
var children = EFDatabase2.Children.Where(c => ids.Contains(c.ccid)).Select(c => new ChildClass { pcid = c.pcid, ccid = c.ccid }).ToLookup(c => c.pcid);

foreach (var parent in parents)
{
    parent.Children = children[parent.pcid];
}

在这种情况下,您只需对数据库执行两次查询。

【讨论】:

  • 这与我在几个实例中所做的非常相似,并且性能“不错”,但是我认为我过于依赖 linq 和 List。我会按照您的描述尝试。
  • 在没有 LINQ 和 List 的情况下,您能否获得任何真正的性能改进是非常值得怀疑的。正如 Pierre-Alain 在您的多个数据库查询中所说的那样,问题是。最好的方法是一次性查询数据库中的所有内容(例如通过存储过程),然后将其映射到您的数据。您应该验证的另一件事是您的数据库中有适当的索引用于查询,可能是您的性能问题存在。
猜你喜欢
  • 1970-01-01
  • 2019-10-06
  • 2014-01-17
  • 1970-01-01
  • 2013-03-05
  • 2019-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多