sta*_*ser 1051 c# linq ienumerable iqueryable linq-to-sql
返回IQueryable<T>与IEnumerable<T>?有什么区别?
IQueryable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
IEnumerable<Customer> custs = from c in db.Customers
where c.City == "<City>"
select c;
Run Code Online (Sandbox Code Playgroud)
两者都会延迟执行,何时应该优先于另一个?
dri*_*iis 1734
是的,两者都会给你延期执行.
区别在于它IQueryable<T>是允许LINQ-to-SQL(LINQ.-to-anything真正)工作的接口.因此,如果您进一步优化查询IQueryable<T>,则该查询将在数据库中执行,如果可能的话.
对于这种IEnumerable<T>情况,它将是LINQ-to-object,这意味着必须将与原始查询匹配的所有对象从数据库加载到内存中.
在代码中:
IQueryable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);
Run Code Online (Sandbox Code Playgroud)
该代码将执行SQL以仅选择黄金客户.另一方面,以下代码将在数据库中执行原始查询,然后过滤掉内存中的非黄金客户:
IEnumerable<Customer> custs = ...;
// Later on...
var goldCustomers = custs.Where(c => c.IsGold);
Run Code Online (Sandbox Code Playgroud)
这是一个非常重要的区别,并且IQueryable<T>在许多情况下可以使您无法从数据库中返回太多行.另一个主要的例子是进行分页:如果你使用Take和Skipon IQueryable,你将只获得所请求的行数; 在a上执行此操作IEnumerable<T>将导致所有行都加载到内存中.
Jac*_*cob 288
最好的答案是好的,但它没有提到解释两个接口"如何"不同的表达式树.基本上,有两组相同的LINQ扩展.Where(),Sum(),Count(),FirstOrDefault(),等都有两个版本:一个接受函数和一个接受表达式.
该IEnumerable版本的签名是:Where(Func<Customer, bool> predicate)
该IQueryable版本的签名是:Where(Expression<Func<Customer, bool>> predicate)
您可能已经使用了两者而没有意识到它,因为两者都使用相同的语法调用:
例如Where(x => x.City == "<City>")适用于IEnumerable和IQueryable
在集合Where()上使用时IEnumerable,编译器将已编译的函数传递给Where()
在集合Where()上使用时IQueryable,编译器将表达式树传递给Where().表达式树就像反射系统,但代码.编译器将您的代码转换为数据结构,该数据结构以易于理解的格式描述您的代码所执行的操作.
为什么要打扰这个表达式树呢?我只是想Where()过滤我的数据.
主要原因是EF和Linq2SQL ORM都可以将表达式树直接转换为SQL,您的代码执行速度会快得多.
哦,这听起来像一个免费的性能提升,我应该AsQueryable()在那种情况下使用所有的地方?
不,IQueryable仅在基础数据提供者可以对其执行某些操作时才有用.像一个普通的东西转换List到IQueryable不会给你带来任何好处.
Kas*_*oma 77
是的,都使用延期执行.让我们用SQL Server探查器来说明差异....
当我们运行以下代码时:
MarketDevEntities db = new MarketDevEntities();
IEnumerable<WebLog> first = db.WebLogs;
var second = first.Where(c => c.DurationSeconds > 10);
var third = second.Where(c => c.WebLogID > 100);
var result = third.Where(c => c.EmailAddress.Length > 11);
Console.Write(result.First().UserName);
Run Code Online (Sandbox Code Playgroud)
在SQL Server探查器中,我们找到一个等于的命令:
"SELECT * FROM [dbo].[WebLog]"
Run Code Online (Sandbox Code Playgroud)
对具有100万条记录的WebLog表运行该代码块大约需要90秒.
因此,所有表记录都作为对象加载到内存中,然后与每个.Where()一起,它将是内存中针对这些对象的另一个过滤器.
当我们使用IQueryable而不是IEnumerable在上面的例子中(第二行):
在SQL Server探查器中,我们找到一个等于的命令:
"SELECT TOP 1 * FROM [dbo].[WebLog] WHERE [DurationSeconds] > 10 AND [WebLogID] > 100 AND LEN([EmailAddress]) > 11"
Run Code Online (Sandbox Code Playgroud)
使用此代码块大约需要四秒钟IQueryable.
IQueryable有一个名为property的属性Expression,它存储一个树表达式,当我们result在我们的示例中使用它时,它开始被创建(称为延迟执行),最后这个表达式将被转换为SQL查询以在数据库引擎上运行.
Jus*_*ner 56
两者都会给你延期执行,是的.
至于哪个优先于另一个,它取决于您的基础数据源是什么.
返回一个IEnumerable将自动强制运行时使用LINQ to Objects来查询您的集合.
返回IQueryable(IEnumerable顺便说一下,实现)提供了额外的功能,可以将查询转换为可能在底层源(LINQ to SQL,LINQ to XML等)上执行得更好的东西.
seb*_*ler 29
一般来说,我会建议如下:
返回IQueryable<T>如果你想用你的方法来完善你在执行前返回查询,让开发人员.
返回IEnumerable如果要运输一组对象来枚举.
想象一下IQueryable它是什么 - 数据的"查询"(如果你愿意,你可以改进).An IEnumerable是一组可以枚举的对象(已经接收或创建过).
Arm*_*ian 25
之前已经说过很多,但是以更技术性的方式回到根源:
IEnumerable 是一个可以枚举的内存中的对象集合 - 一个内存序列,可以进行迭代(在foreach循环中使它变得容易,尽管你IEnumerator只能使用它).它们按原样驻留在内存中.IQueryable 是一个表达式树,它将在某个时刻被翻译成其他东西,能够枚举最终结果.我想这就是让大多数人感到困惑的原因.他们显然有不同的内涵.
IQueryable表示一个表达式树(简单地称为查询),一旦调用了释放API,它就会被底层查询提供者转换为其他东西,比如LINQ聚合函数(Sum,Count等)或ToList [Array,Dictionary ,. ..].并且IQueryable对象也实现IEnumerable,IEnumerable<T>因此如果它们表示查询,则可以迭代该查询的结果.这意味着IQueryable不必仅是查询.正确的术语是表达树.
现在,如何执行这些表达式以及它们转向什么都取决于所谓的查询提供程序(表达式执行程序,我们可以认为它们).
在Entity Framework世界中(这是神秘的底层数据源提供程序或查询提供程序)IQueryable表达式被转换为本机T-SQL查询.Nhibernate与他们做类似的事情.您可以按照LINQ:构建IQueryable Provider链接中描述的概念编写自己的概念,并且您可能希望为产品存储提供程序服务提供自定义查询API.
所以基本上,IQueryable对象的构造一直很长,直到我们明确地释放它们并告诉系统将它们重写为SQL或其他任何东西并向下发送执行链以进行后续处理.
好像要延迟执行它是一种LINQ功能,可以在内存中保存表达式树方案,并在需要对序列调用某些API(相同的Count,ToList等)时根据需要将其发送到执行中.
两者的正确使用在很大程度上取决于您针对特定案例所面临的任务.对于众所周知的存储库模式,我个人选择返回IList,即IEnumerable列表(索引器等).所以我的建议IQueryable只在代码库中的存储库和IEnumerable中使用.没有说关于可破坏性问题的IQueryable分解和破坏关注点分离的原则.如果从存储库中返回表达式,则消费者可以按照自己的意愿使用持久层.
混乱的一点点:)(来自评论中的讨论))它们都不是内存中的对象,因为它们本身并不是真正的类型,它们是一种类型的标记 - 如果你想深入了解它.但是,将MSnumerables视为内存中的集合而将IQueryables视为表达式树,这是有意义的(这就是为什么即使是MSDN也这样说).关键是IQueryable接口继承IEnumerable接口,因此如果它表示查询,则可以枚举该查询的结果.枚举导致与IQueryable对象关联的表达式树被执行.所以,事实上,如果没有内存中的对象,你就无法真正调用任何IEnumerable成员.如果你这样做,它会进入那里,无论如何,如果它不是空的.IQueryables只是查询,而不是数据.
小智 24
通常,您希望保留查询的原始静态类型,直到重要为止.
因此,您可以将变量定义为"var"而不是其中任何一个IQueryable<>,IEnumerable<>并且您将知道您没有更改类型.
如果你从a开始IQueryable<>,你通常希望保持它IQueryable<>为止,直到有一些令人信服的理由改变它.这样做的原因是您希望为查询处理器提供尽可能多的信息.例如,如果您只打算使用10个结果(您已调用Take(10)),那么您希望SQL Server知道这一点,以便它可以优化其查询计划并仅向您发送您将使用的数据.
一个令人信服的理由更改从类型IQueryable<>到IEnumerable<>可能是你拨打的实现的一些扩展功能IQueryable<>在您的特定对象既不能处理或低效处理.在这种情况下,您可能希望将类型转换为IEnumerable<>(通过分配类型的变量IEnumerable<>或通过使用AsEnumerable扩展方法),以便您调用的扩展函数最终成为Enumerable类中的扩展函数而不是Queryable类.
Ole*_*kyi 18
有一篇博客文章,内容简要介绍了如何滥用IEnumerable<T>可能会极大地影响LINQ查询性能:实体框架:IQueryable与IEnumerable.
如果我们深入挖掘并查看来源,我们可以看到有明显不同的扩展方法IEnumerable<T>:
// Type: System.Linq.Enumerable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Enumerable
{
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
return (IEnumerable<TSource>)
new Enumerable.WhereEnumerableIterator<TSource>(source, predicate);
}
}
Run Code Online (Sandbox Code Playgroud)
并且IQueryable<T>:
// Type: System.Linq.Queryable
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Core.dll
public static class Queryable
{
public static IQueryable<TSource> Where<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate)
{
return source.Provider.CreateQuery<TSource>(
Expression.Call(
null,
((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(
new Type[] { typeof(TSource) }),
new Expression[]
{ source.Expression, Expression.Quote(predicate) }));
}
}
Run Code Online (Sandbox Code Playgroud)
第一个返回可枚举迭代器,第二个通过查询提供程序创建查询,在IQueryablesource中指定.
ssc*_*der 11
我最近遇到了有问题的IEnumerable诉IQueryable.所使用的算法首先执行IQueryable查询以获得一组结果.然后将这些项传递给foreach循环,将项实例化为实体框架(EF)类.然后在fromLinq to Entity查询的子句中使用此EF类,从而导致结果IEnumerable.
我是EF和Linq for Entities的新手,所以需要一段时间来弄清楚瓶颈是什么.使用MiniProfiling,我找到了查询,然后将所有单个操作转换为单个IQueryableLinq for Entities查询.在IEnumerable花了15秒,IQueryable花了0.5秒执行.涉及到三个表,在阅读完之后,我认为IEnumerable查询实际上形成了一个三表交叉产品并过滤结果.
尝试使用IQueryables作为经验法则,并对您的工作进行分析,以使您的更改可衡量.
Ale*_*ard 10
我想澄清一些看似相互矛盾的反应(主要是围绕IEnumerable).
(1)IQueryable扩展IEnumerable界面.(你可以发送一个没有错误的IQueryable东西IEnumerable.)
(2)在迭代结果集时,两者IQueryable和IEnumerableLINQ都尝试延迟加载.(请注意,可以在每种类型的接口扩展方法中看到实现.)
换句话说,IEnumerables不仅仅是"记忆中". IQueryables并不总是在数据库上执行. IEnumerable必须将内容加载到内存中(一旦检索,可能是懒惰),因为它没有抽象数据提供程序. IQueryables依赖于抽象提供程序(如LINQ-to-SQL),尽管这也可能是.NET内存提供程序.
示例用例
(a)IQueryable从EF上下文中检索记录列表.(没有记录在内存中.)
(b)传递IQueryable给模型的视图IEnumerable.(有效.IQueryable延伸IEnumerable.)
(c)从视图中迭代并访问数据集的记录,子实体和属性.(可能会导致异常!)
可能的问题
(1) IEnumerable尝试延迟加载和您的数据上下文已过期.抛出异常,因为提供程序不再可用.
(2)启用实体框架实体代理(默认),并尝试访问具有过期数据上下文的相关(虚拟)对象.与(1)相同.
(3)多个活动结果集(MARS).如果您IEnumerable在foreach( var record in resultSet )块中迭代并同时尝试访问record.childEntity.childProperty,则由于数据集和关系实体的延迟加载,您可能最终得到MARS.如果未在连接字符串中启用,则会导致异常.
解
通过调用执行查询并存储结果resultList = resultSet.ToList() 这似乎是确保实体在内存中最直接的方法.
在您访问相关实体的情况下,您可能仍需要数据上下文.要么,或者您可以Include从您的实体中禁用实体代理和明确相关的实体DbSet.
小智 6
我们可以以相同的方式使用这两种方法,它们的性能只是不同。
IQueryable仅以有效的方式对数据库执行。这意味着它将创建一个完整的选择查询,并且仅获取相关记录。
例如,我们要选择名称以“ Nimal”开头的前10位客户。在这种情况下,选择查询将生成为select top 10 * from Customer where name like ‘Nimal%’。
但是,如果我们使用IEnumerable,则查询将类似于select * from Customer where name like ‘Nimal%’,并且前十名将在C#编码级别进行过滤(它将从数据库中获取所有客户记录,并将它们传递到C#中)。
除了前两个非常好的答案(由driis和Jacob撰写):
IEnumerable接口在System.Collections命名空间中。
IEnumerable对象表示内存中的一组数据,并且只能向前移动该数据。由IEnumerable对象表示的查询将立即完整执行,因此应用程序可以快速接收数据。
执行查询时,IEnumerable会加载所有数据,并且如果需要过滤,则过滤本身是在客户端完成的。
IQueryable接口位于System.Linq命名空间中。
IQueryable对象提供对数据库的远程访问,并允许您以直接的顺序从头到尾或以相反的顺序浏览数据。在创建查询的过程中,返回的对象是IQueryable,查询已得到优化。结果,在执行过程中消耗的内存更少,网络带宽也更少,但是与此同时,它的处理速度比返回IEnumerable对象的查询要慢一些。
选择什么?
如果您需要整个返回的数据集,则最好使用IEnumerable,它可以提供最快的速度。
如果您不需要整个返回的数据集,而只需要一些过滤后的数据,那么最好使用IQueryable。