【问题标题】:What collection should I use in a linq-to-sql query? Queryable vs Enumerable vs List我应该在 linq-to-sql 查询中使用什么集合? Iqueryable vs Ienumerable vs List
【发布时间】:2015-11-25 14:08:24
【问题描述】:

想象以下类:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Underage
{
    public int Age { get; set; }
}

我会这样做:

var underAge = db.Underage.Select(u => u.Age)   .ToList()/AsEnumerable()/AsQueryable() 

var result = db.Persons.Where(p => underAge.Contains(p.Age)).ToList();

什么是最好的选择?如果我调用ToList(),这些项目将被检索一次,但如果我选择AsEnumerableAsQueryable,它们将在每次从Where() 的数据库中选择一个人时执行(如果它是这样工作的,我不不太了解数据库在后台做什么)?

Underage 包含数千条记录与少量记录是否有更大的区别?

【问题讨论】:

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


    【解决方案1】:

    无。

    您绝对不希望在此处使用ToList(),因为这会将所有匹配值加载到内存中,然后使用它为result 编写一个查询,其中包含大量IN (…) 子句。当您真正想要的是首先从数据库中获取匹配值时,这是一种浪费,查询类似于

    SELECT *
    FROM Persons
    WHERE EXISTS(
      SELECT NULL FROM 
      FROM Underage
      WHERE Underage.age = Persons.age
    )
    

    AsEnumerable() 通常会阻止对数据库执行操作,尽管提供者可能会检查源并撤消该AsEnumerable() 的影响。 AsQueryable() 很好,但在这种情况下实际上并没有做任何事情。

    你不需要它们中的任何一个:

    var underAge = db.Underage.Select(u => u.Age);
    
    var result = db.Persons.Where(p => underAge.Contains(p.Age)).ToList();
    

    (就此而言,请检查您是否确实需要最后一行中的 ToList();如果您要对其进行进一步查询,如果您只是要枚举,它可能会伤害他们结果是浪费时间和内存,如果您要存储它们或进行大量在 Linq 中无法完成的内存中操作,那就更好了)。

    【讨论】:

    • 那么当我在第二行调用ToList时,我的程序只会执行一个查询?
    • 是的,执行一次,执行与我上面展示的 SQL 相当的操作,然后生成一个列表。但是,如果您只是对它执行了foreach,它会执行 SQL,然后在每个对象到来时使用它们,这通常更快。如果您没有执行ToList()foreach,那么您将有另一个可以进一步组合的查询。例如。通过添加另一个Where() 或调用Count() 之类的东西,大部分工作都可以在数据库中完成,而不是全部完成。
    • 谢谢!我会研究那个 foreach,从来没有在 Linq 中使用过它
    • 98% 的时间,如果可能的话,你使用 linq 的最终结果应该是调用一个聚合(Count()Max() 或其他给出单一结果的东西)或者直接通过结果直接foreach。如果您真的需要该集合,请仅调用集合生成方法(ToList()ToDictionary()ToLookup())。有一些例外情况(特别是 一些 使用 GroupBy 会在内存中更有效),但始终默认为尽可能少地拉入内存。
    • 一般规则是尽可能长时间地保持对象尽可能接近原始对象。 DbSet > IQueryable > IEnumerable > 列表/数组。仅在有理由需要较低的一种而较高的一种不起作用时才使用较低的一种。
    【解决方案2】:

    如果您只是将第一个查询保留为

      var underage = db.Underage.Select(u => u.Age);
    

    它将是 IQueryable 类型,并且不会 ping 您的数据库(准确称为延迟执行)。一旦您真正想要执行对数据库的调用,您可以使用贪心运算符,例如 .ToList()、.ToArray()、.ToDictionary()。这将为您的结果变量提供一个 IEnumerable 集合。

    See linq deferred execution

    【讨论】:

    • 谢谢!留下这样的代码和添加 .AsQueryable() 有区别吗?我想不会
    • 只有添加AsQueryable() 才能在您稍后查看代码并想知道它的用途时混淆问题。
    【解决方案3】:

    您应该使用连接来获取数据。

    var query = (from p in db.Persons
                 join u in db.Underage
                  on u.Age equals p.Age
                 select p).ToList();
    

    如果您在上面的查询中没有使用.ToList(),它将返回Person 类型的IEnumerable,并且在您使用该对象时将获取实际数据。

    根据您要使用的场景,ToList()、AsEnurmeable 和 Querable 都一样好。对于您的情况,ToList 对我来说看起来不错,因为您只想获取未成年人的列表

    【讨论】:

    • 已编辑,但我想知道在上面的示例中使用 Linq 表达式最好的是什么。
    • 一定有区别。如果我将其设为 IQueryable,它是将该查询“附加”到第二个查询还是像我使用 ToList() 时那样执行单独的查询?
    • 如果underage 中有任何重复的年龄,则加入将不正确。要匹配问题中的查询结果,您必须将其作为没有重复年龄的已知不变量,或者在此使用 Distinct()
    猜你喜欢
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    • 2011-04-27
    • 2011-04-05
    • 2014-06-05
    • 1970-01-01
    相关资源
    最近更新 更多