5

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

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();
}
4

2 回答 2

14

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

一种选择是返回一个IQueryable<DbItems>

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

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

相反,我会创建一个返回的方法IQueryable

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

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

public IEnumerable<DbItems> GetFoo()
{
    return GetFooQuery().AsEnumerable();
}
于 2013-08-08T21:37:37.907 回答
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 查询。

于 2013-08-08T21:35:03.843 回答