Jon*_*han 5 c# sql sql-server entity-framework entity-framework-5
如果我GetFoo().Count()
从我的数据库类之外的方法调用,实体框架5执行的查询效率相当低,而不是COUNT.从阅读像其他一些问题,这个,我看这是预期的行为.
public IEnumerable<DbItems> GetFoo()
{
return context.Items.Where(d => d.Foo.equals("bar"));
}
Run Code Online (Sandbox Code Playgroud)
因此我在我的DB类中添加了一个count方法,它正确执行COUNT查询:
public int GetFooCount()
{
return context.Items.Where(d => d.Foo.equals("bar")).Count();
}
Run Code Online (Sandbox Code Playgroud)
为了避免多次指定查询,我想将其更改为以下内容.然而,这再次执行SELECT,即使它在DB类中.这是为什么 - 我怎么能避免它?
public int GetFooCount()
{
return this.GetFoo().Count();
}
Run Code Online (Sandbox Code Playgroud)
D S*_*ley 14
因为GetFoo()
返回a IEnumerable<DbItems>
,查询作为a执行SELECT
,然后Count
应用于对象集合,而不是投影到SQL.
一种选择是返回IQueryable<DbItems>
:
public IQueryable<DbItems> GetFoo()
{
return context.Items.Where(d => d.Foo.equals("bar"));
}
Run Code Online (Sandbox Code Playgroud)
但是这可能会改变其他调用者的行为,这些调用者期望加载集合(IQueryable
它将被延迟加载).特别是添加.Where
无法转换为SQL的调用的方法.遗憾的是,您无法在编译时了解这些内容,因此需要进行全面测试.
我会创建一个返回以下内容的新方法IQueryable
:
public IQueryable<DbItems> GetFooQuery()
{
return context.Items.Where(d => d.Foo.equals("bar"));
}
Run Code Online (Sandbox Code Playgroud)
所以你现有的用法不会受到影响.如果您想重新使用该代码,可以更改GetFoo
为:
public IEnumerable<DbItems> GetFoo()
{
return GetFooQuery().AsEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
Ser*_*kiy 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;
}
Run Code Online (Sandbox Code Playgroud)
但是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
Run Code Online (Sandbox Code Playgroud)
执行,然后用上面的方法计算在内存中计算的项目.但是如果你将返回类型更改为IQueryable<T>
,那么所有更改
public IQueryable<DbItems> GetFoo()
{
return context.Items.Where(d => d.Foo.equals("bar"));
}
Run Code Online (Sandbox Code Playgroud)
现在Queryable<T>.Count()
执行了.这意味着查询继续构建(嗯,实际上Count()
是强制执行查询但是Count()
成为此查询的一部分的运算符).和EF翻译
context.Items.Where(d => d.Foo.equals("bar")).Count()
Run Code Online (Sandbox Code Playgroud)
进入在服务器端执行的SQL查询.