实体框架 - COUNT而不是SELECT

时间:2013-08-08 21:32:49

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

如果我从我的数据库类之外的方法调用GetFoo().Count(),则实体框架5执行的查询效率相当低,而不是COUNT。通过阅读其他一些问题,例如this,我发现这是预期的行为。

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

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

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

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

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

2 个答案:

答案 0 :(得分:14)

由于GetFoo()返回IEnumerable<DbItems>,查询将作为SELECT执行,然后Count将应用于对象集合,而不会投影到SQL。

一个选项是返回IQueryable<DbItems>

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

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

我会创建一个返回IQueryable new 方法:

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

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

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

答案 1 :(得分:13)

要了解此行为,您需要了解IEnumerable<T>IQueryable<T>扩展程序之间的区别。第一个使用Linq to Objects,它是内存中的查询。此查询未转换为SQL,因为这是简单的.NET代码。因此,如果您有一些IEnumerable<T>值,并且您正在执行Count(),则会调用Enumerable.Count扩展方法,如下所示:

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

    return num;
}

IQueryable<T>扩展名的故事完全不同。这些方法由底层LINQ提供程序(在您的情况下为EF)转换为.NET代码以外的其他方法。例如。到SQL。执行查询时会发生此转换。分析所有查询,并生成好的(好吧,并不总是很好)SQL。此SQL在数据库中执行,结果将作为查询执行结果返回给您。

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

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

执行,然后使用上述方法计算内存中计算的项目数。但是,如果您将返回类型更改为IQueryable<T>,则所有更改

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

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

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

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