【问题标题】:Entity framework - COUNT rather than SELECT实体框架 - COUNT 而不是 SELECT
【发布时间】:2013-08-10 18:51:30
【问题描述】:

如果我从我的 DB 类之外的方法调用 GetFoo().Count(),Entity Framework 5 会执行相当低效的 SELECT 查询而不是 COUNT。通过阅读诸如this 之类的其他一些问题,我发现这是预期的行为。

public IEnumerable<DbItems> GetFoo()
{
    return context.Items.Where(d => d.Foo.equals("bar"));
}

因此,我在我的 DB 类中添加了一个 count 方法,它可以正确执行 COUNT 查询:

public int GetFooCount()
{
    return context.Items.Where(d => d.Foo.equals("bar")).Count();
}

为了避免我多次指定查询,我想将其更改为以下内容。然而,这再次执行 SELECT,即使它在 DB 类中。为什么会这样 - 我该如何避免?

public int GetFooCount()
{
    return this.GetFoo().Count();
}

【问题讨论】:

  • Context.Items.Count(d => d.Foo == "bar");你不需要 Where 调用:)

标签: c# sql sql-server entity-framework entity-framework-5


【解决方案1】:

为了理解这种行为,您需要了解IEnumerable&lt;T&gt;IQueryable&lt;T&gt; 扩展之间的区别。第一个与 Linq to Objects 一起使用,这是内存中的查询。此查询不会转换为 SQL,因为这是简单的 .NET 代码。因此,如果您有一些 IEnumerable&lt;T&gt; 值,并且您正在执行 Count() 这将调用 Enumerable.Count 扩展方法,类似于:

public static int Count<TSource>(this IEnumerable<TSource> source)
{   
    int num = 0;
    foreach(var item in source)
        num++;

    return num;
}

但是IQueryable&lt;T&gt; 扩展的故事完全不同。这些方法由底层 LINQ 提供程序(在您的情况下为 EF)转换为 .NET 代码以外的内容。例如。到 SQL。当您执行查询时,就会发生这种翻译。分析所有查询,并生成好(当然,并不总是好)的 SQL。此 SQL 在数据库中执行,结果作为查询执行的结果返回给您。

因此,您的方法返回 IEnumerable&lt;T&gt; - 这意味着您正在使用应该在内存中执行的 Enumerable.Count() 方法。因此,EF 将以下查询翻译成 SQL

context.Items.Where(d => d.Foo.equals("bar")) // translated into SELECT WHERE

执行,然后使用上述方法在内存中计算的项目计数。但是如果你将返回类型更改为IQueryable&lt;T&gt;,那么所有更改

public IQueryable<DbItems> GetFoo()
{
    return context.Items.Where(d => d.Foo.equals("bar"));
}

现在Queryable&lt;T&gt;.Count() 被执行。这意味着查询继续构建(实际上Count() 是强制执行查询的运算符,但Count() 成为此查询的一部分)。而EF翻译

context.Items.Where(d => d.Foo.equals("bar")).Count()

进入在服务器端执行的 SQL 查询。

【讨论】:

  • 嗨!我试过弄乱 .AsQueryable().Count() 和 .AsEnumerable().Count() ,我得到的性能统计数据是一样的。例如。 - 在 MySql 工作台中“从苹果中选择 count(*)”~1ms - 在 EF linq 中“dbContext.apples.Count()”~800ms
  • @Ross 我认为您应该创建包含所有必需信息的新问题。例如。如果这是您的第一个查询,那么 EF 需要一些时间来构建 edm
【解决方案2】:

由于GetFoo() 返回IEnumerable&lt;DbItems&gt;,因此查询作为SELECT 执行,然后Count 应用于对象集合,而不是投影到SQL。

一个选项是返回IQueryable&lt;DbItems&gt;

public IQueryable<DbItems> GetFoo()
{
    return context.Items.Where(d => d.Foo.equals("bar"));
}

但这可能会改变期望加载集合的其他调用者的行为(IQueryable 它将被延迟加载)。特别是添加无法转换为 SQL 的 .Where 调用的方法。不幸的是,您在编译时不会知道这些,因此需要进行彻底的测试。

我会改为创建一个返回IQueryablenew 方法:

public IQueryable<DbItems> GetFooQuery()
{
    return context.Items.Where(d => d.Foo.equals("bar"));
}

因此您现有的使用不会受到影响。如果您想重用该代码,您可以将 GetFoo 更改为:

public IEnumerable<DbItems> GetFoo()
{
    return GetFooQuery().AsEnumerable();
}

【讨论】:

    猜你喜欢
    • 2014-04-01
    • 2014-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多