cho*_*bo2 62 c# linq ienumerable list iqueryable
我想知道IQueryable,List,IEnumerator和我何时应该使用每个的区别?
例如,当使用Linq to SQL时,我会做这样的事情:
public List<User> GetUsers()
{
return db.User.where(/* some query here */).ToList();
}
Run Code Online (Sandbox Code Playgroud)
现在我想知道我是否应该使用IQueryable.我不确定在列表中使用它的好处.
Ada*_*son 110
IQueryable<T>旨在允许查询提供程序(例如,LINQ to SQL或实体框架之类的ORM)使用查询中包含的表达式将请求转换为另一种格式.换句话说,LINQ-to-SQL会查看您正在使用的实体的属性以及您正在进行的比较,并实际创建一个SQL语句来表达(希望)一个等效的请求.
IEnumerable<T>比更通用的IQueryable<T>(尽管所有实例IQueryable<T>执行IEnumerable<T>),并且仅定义序列.但是,类中有一些扩展方法可以在该Enumerable接口上定义一些查询类型操作符,并使用普通代码来评估这些条件.
List<T>只是一种输出格式,虽然它实现IEnumerable<T>,但与查询没有直接关系.
换句话说,当你使用IQueryable<T>,你定义一个表达的是被翻译成别的东西.即使您正在编写代码,该代码也永远不会被执行,它只会被检查并变成其他东西,就像实际的SQL查询一样.因此,只有某些内容在这些表达式中有效.例如,您无法调用从这些表达式中定义的普通函数,因为LINQ-to-SQL不知道如何将调用转换为SQL语句.不幸的是,大多数这些限制仅在运行时进行评估.
当您使用IEnumerable<T>查询时,您正在使用LINQ到对象,这意味着您正在编写用于评估查询或转换结果的实际代码,因此通常对您可以执行的操作没有任何限制.您可以自由地在这些表达式中调用其他函数.
使用LINQ to SQL
与上述区别密切相关,牢记在实践中如何运作也很重要.当您在LINQ to SQL中针对数据上下文类编写查询时,它会生成一个IQueryable<T>.无论你对IQueryable<T>自己做什么都会变成SQL,所以你的过滤和转换将在服务器上完成.不管你做什么反对这种作为IEnumerable<T>,将在应用层来完成.有时这是可取的(例如,在您需要使用客户端代码的情况下),但在许多情况下这是无意的.
例如,如果我有一个Customers表示Customer表的属性的上下文,并且每个客户都有一个CustomerId列,那么让我们看看执行此查询的两种方法:
var query = (from c in db.Customers where c.CustomerId == 5 select c).First();
Run Code Online (Sandbox Code Playgroud)
这将生成用于查询数据库以获取等于5 的Customer记录的SQL CustomerId.类似于:
select CustomerId, FirstName, LastName from Customer where CustomerId = 5
Run Code Online (Sandbox Code Playgroud)
现在,如果我们使用扩展方法Customers变成一个会发生什么?IEnumerable<Customer>AsEnumerable()
var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First();
Run Code Online (Sandbox Code Playgroud)
这种简单的改变会产生严重后果.由于我们正在Customers变成一个IEnumerable<Customer>,这将把整个表格带回来并在客户端过滤它(严格来说,这会带回表中的每一行,直到它遇到符合标准的那一行,但重点是相同).
ToList()
到目前为止,我们只谈过IQueryable和IEnumerable.这是因为它们是类似的,互补的接口.在这两种情况下,您都要定义一个查询 ; 也就是说,你定义在那里找到的数据,什么过滤应用,以及哪些数据返回.这两个都是查询
query = from c in db.Customers where c.CustomerId == 5 select c;
query = from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c;
Run Code Online (Sandbox Code Playgroud)
就像我们所讨论的那样,第一个查询正在使用IQueryable,第二个查询正在使用IEnumerable.但是,在这两种情况下,这只是一个查询.定义查询实际上不会对数据源执行任何操作.当代码开始迭代列表时,实际执行查询.这可以通过多种方式发生; 一个foreach循环,调用ToList()等.
查询在第一次和每次迭代时执行.如果你要调用ToList()上query两倍,你最终会得到两个清单完全不同的对象.它们可能包含相同的数据,但它们可能是不同的引用.
评论后编辑
我只想清楚地了解客户端何时完成以及何时完成服务器端之间的区别.如果你引用的IQueryable<T>是一个IEnumerable<T>,那么只有在它完成后IEnumerable<T>才会在客户端完成查询.例如,假设我有这个表和LINQ-to-SQL上下文:
Customer
-----------
CustomerId
FirstName
LastName
Run Code Online (Sandbox Code Playgroud)
我首先构建一个基于的查询FirstName.这会创建一个IQueryable<Customer>:
var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c;
Run Code Online (Sandbox Code Playgroud)
现在我将该查询传递给一个函数,该函数IEnumerable<Customer>根据以下内容进行并进行一些过滤LastName:
public void DoStuff(IEnumerable<Customer> customers)
{
foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
{
Console.WriteLine(cust.CustomerId);
}
}
Run Code Online (Sandbox Code Playgroud)
我们在这里做了第二次查询,但它正在完成IEnumerable<Customer>.这里会发生的是第一个查询将被评估,运行这个SQL:
select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%'
Run Code Online (Sandbox Code Playgroud)
因此,我们将把所有FirstName开始的人带回来"Ad".请注意,这里没有任何内容LastName.那是因为它被客户端过滤掉了.
一旦它带回这些结果,程序将迭代结果并仅传递其LastName开头的记录"Ro".这样做的缺点是我们带回了数据 - 即所有LastName 不以其开头的行"Ro"- 可能已在服务器上过滤掉了.
IQueryable<T>:摘要数据库访问,支持对查询的延迟评估
List<T>:条目集合.不支持懒惰的评价
IEnumerator<T>:提供迭代的能力IEnumerable<T>(这两个IQueryable<T>和List<T>是)
该代码的问题非常简单 - 它总是在调用时执行查询.如果你要返回db.User.Where(...)(这是一个IQueryable<T>),你将持有对查询的评估,直到它实际需要(迭代).此外,如果该方法的用户需要指定更多谓词,那么这些谓词也会在数据库中执行,这样会更快.