C# IEnumerable<;T>;处理而不是列表<;T>;,延迟执行会导致问题吗

C# IEnumerable<;T>;处理而不是列表<;T>;,延迟执行会导致问题吗,c#,linq,list,ienumerable,C#,Linq,List,Ienumerable,在代码中,我处理IEnumerable,将其持久化到内存缓存中,在那里它第一次进入数据库并以IEnumerable的形式获取结果。数据处理包括以下操作: 过滤不同列上的数据 多列对过滤后的数据进行排序并获取子集(1页=100条记录) 计算过滤数据的数值列摘要,如Sum、Avg、Min、Max 总的来说,数据处理是一个相当复杂的逻辑,包括使用反射和表达式树根据用户输入和运算符进行过滤 到目前为止,我第一次使用ToList()将IEnumerable转换为List,以便于处理,然后将IEnumera

在代码中,我处理
IEnumerable
,将其持久化到内存缓存中,在那里它第一次进入数据库并以
IEnumerable
的形式获取结果。数据处理包括以下操作:

  • 过滤不同列上的数据
  • 多列对过滤后的数据进行排序并获取子集(1页=100条记录)
  • 计算过滤数据的数值列摘要,如Sum、Avg、Min、Max
  • 总的来说,数据处理是一个相当复杂的逻辑,包括使用反射和表达式树根据用户输入和运算符进行过滤

    到目前为止,我第一次使用
    ToList()
    IEnumerable
    转换为
    List
    ,以便于处理,然后将
    IEnumerable
    转换为
    List
    ,因为所有Linq和其他操作的结果都是
    IEnumerable
    。现在,随着系统中的数据开始增加,甚至会影响性能,因为在200-300万条记录上执行
    ToList()
    需要2-3秒,这是我们想要避免的,因为我们试图在1-2秒完成调用,包括将数据呈现给用户,最终用户只获得100条记录。谨慎的做法是对最后100条记录进行内存分配

    IEnumerable
    处理相关的重要问题如下:

  • 它是延迟执行,这意味着只有当我们想要处理结果时,才会处理它上的各种Linq操作。从数据库中获取数据也是如此。我的理解是否定的,因为这是一个阻塞操作,将获取内存流,所以延迟执行只会是Linq to对象的问题,在这种情况下,我们也需要小心闭包问题
  • Count()
    foreach
    这样的操作,它们会导致
    IEnumerable
    分配类似于
    ToList()
    的内存,还是会处理挂起的延迟操作,然后只使用枚举器处理结果
  • 将整体处理完全转移到
    IEnumerable
    ,这肯定会限制API的访问,这是否是一个更好的策略,但我发现另一个挑战是,在开始时提到的两个操作排序分页和摘要计算是在过滤数据上使用任务API并行完成的,这是否会使它们各自单独执行数据过滤,即大约将200万条记录过滤到100 K,从而导致更多的性能问题
  • 编辑1:(澄清)


    我使用Dapper.Net从数据库中获取数据,该数据库在内部将一个列表包装在IEnumerable中,因此可用于进一步处理

    如果您将数据库查询的结果作为一个
    IEnumerable
    ,那么您已经做错了什么。在拥有可枚举项时,内存中已经有了完整的项集,因此没有理由不将其作为列表或数组。通过获取IEnumerable并对其调用ToList,您所做的就是将它从一个已经存在的数组复制到一个新数组中。您应该查看您的查询,看看如何对其进行优化,以立即返回列表并避免这种重复-allocation@caesay在有可枚举项时,内存中已经有了完整的项集???什么??为什么?这取决于他用来返回结果集的技术。。。显然存在一些限制,但是如果保持连接打开,您可以轻松地将
    SqlDataReader
    转换为
    IEnumerable
    ,并且在内存中一次只有一行。@m最后一个像Count()或foreach这样的操作是否会导致IEnumerable分配类似于ToList()的内存你对IEnumerable的理解可能很低。。。枚举(甚至
    Count()
    ing)内存中没有列表/数组/集合支持的
    IEnumerable
    会导致“重新生成”。。。因此,如果您Count()+枚举它以显示它,可能会导致执行两个查询。永远不要以任何方式枚举两次
    IEnumerable
    (即使是
    Count()
    ANY()
    FirstOrDefault()
    ),除非您准备好支付双重执行,或者您确定数据已经在内存中。@xanatos感谢您提供的详细信息,我对IEnumerable的理解并没有那么差,但我肯定没有很好地解释细节。我正在使用Dapper micro ORM获取数据,其查询提供IEnumerable,它内部包含List,所以是的,我已经分配了内存。现在这是否意味着IEnumerable上的所有操作都将由内部分配的数据结构支持,在本例中为List。在这里输入IEnumerable并有效地使用分配的内存进行数据处理,而不是使用IEnumerable@caesay因为我在内部使用Dapper,所以它已经分配了列表,所以您的观点是正确的,我可以通过简单的类型转换使用相同的内存如果数据库查询的结果是
    IEnumerable
    ,那么您已经做错了什么。在拥有可枚举项时,内存中已经有了完整的项集,因此没有理由不将其作为列表或数组。通过获取IEnumerable并对其调用ToList,您所做的就是将它从一个已经存在的数组复制到一个新数组中。您应该查看您的查询,看看如何对其进行优化,以立即返回列表并避免这种重复-allocation@caesay在有可枚举项时,内存中已经有了完整的项集???什么??为什么?这取决于他用来返回结果集的技术。。。显然存在一些限制,但是如果保持连接打开,就可以轻松地转换
    SqlDataR