我想知道iqueryable、list、ienumerator和什么时候应该使用它们之间的区别是什么?
例如,在使用Linq to SQL时,我会这样做:
1 2 3 4
| public List<User> GetUsers()
{
return db.User.where(/* some query here */).ToList();
} |
现在我想知道我是否应该使用iqueryable。我不确定使用它比列表的优势。
IQueryable旨在允许查询提供程序(例如,像linq to sql或实体框架这样的ORM)使用查询中包含的表达式将请求转换为另一种格式。换句话说,Linq to SQL查看正在使用的实体的属性以及正在进行的比较,并实际创建一个SQL语句来表示(希望)一个等效的请求。好的。
IEnumerable比IQueryable更通用(尽管IQueryable的所有实例都实现IEnumerable,并且只定义序列。但是,在Enumerable类中有一些扩展方法可用于定义该接口上的一些查询类型运算符,并使用普通代码来评估这些条件。好的。
List只是一种输出格式,在实现IEnumerable的同时,与查询没有直接关系。好的。
换言之,当您使用IQueryable时,您定义了一个可以转换成其他内容的表达式。即使您正在编写代码,该代码也不会被执行,它只会被检查并转换为其他代码,比如实际的SQL查询。因此,在这些表达式中只有某些内容是有效的。例如,不能从这些表达式中调用定义的普通函数,因为Linq to SQL不知道如何将调用转换为SQL语句。不幸的是,这些限制中的大多数只在运行时进行评估。好的。
当您使用IEnumerable进行查询时,您使用的是linq to对象,这意味着您正在编写用于评估查询或转换结果的实际代码,因此一般来说,您可以做什么没有限制。您可以从这些表达式中自由调用其他函数。好的。
使用Linq to SQL好的。
将上述区别结合在一起,同样重要的是要记住这在实践中是如何实现的。当您在linq to sql中针对数据上下文类编写查询时,它会生成一个IQueryable。无论您对IQueryable本身做什么,都将变成SQL,因此您的过滤和转换将在服务器上完成。无论您作为一个IEnumerable来做什么,都将在应用程序级别完成。有时这是可取的(例如,在需要使用客户端代码的情况下),但在许多情况下这是无意的。好的。
例如,如果我有一个表示Customer表的Customers属性的上下文,并且每个客户都有一个CustomerId列,那么我们来看两种执行此查询的方法:好的。
1
| var query = (from c in db.Customers where c.CustomerId == 5 select c).First(); |
这将生成查询数据库中Customer记录的SQL,其中CustomerId等于5。类似:好的。
1
| select CustomerId, FirstName, LastName from Customer where CustomerId = 5 |
现在,如果使用AsEnumerable()扩展方法将Customers转换成IEnumerable会发生什么?好的。
1
| var query = (from c in db.Customers.AsEnumerable() where c.CustomerId == 5 select c).First(); |
这种简单的改变会产生严重的后果。由于我们将Customers转换为IEnumerable,这将使整个表返回,并在客户端对其进行筛选(严格来说,这将使表中的每一行返回,直到遇到符合条件的行,但要点是相同的)。好的。
托尔斯特()好的。
到目前为止,我们只讨论过IQueryable和IEnumerable。这是因为它们是相似的、免费的接口。在这两种情况下,您都定义了一个查询;也就是说,您定义了查找数据的位置、要应用的过滤器以及要返回的数据。这两个都是查询好的。
1 2
| 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; |
正如我们所讨论的,第一个查询使用IQueryable,第二个查询使用IEnumerable。然而,在这两种情况下,这只是一个查询。定义查询实际上并不针对数据源做任何事情。当代码开始在列表上迭代时,实际上执行查询。这可以通过多种方式发生:一个foreach循环,调用ToList()等。好的。
查询是第一次执行的,每次迭代都会执行一次。如果你在query上打两次ToList(),你会得到两个对象完全不同的列表。它们可能包含相同的数据,但它们是不同的引用。好的。
评论后编辑好的。
我只想清楚什么时候做客户端和什么时候做服务器端之间的区别。如果您将IQueryable引用为IEnumerable,那么只有在它是IEnumerable之后进行的查询才会在客户端进行。例如,假设我有这个表和一个Linq to SQL上下文:好的。
1 2 3 4 5
| Customer
-----------
CustomerId
FirstName
LastName |
我首先构建一个基于FirstName的查询。这就产生了一个IQueryable:好的。
1
| var query = from c in db.Customers where c.FirstName.StartsWith("Ad") select c; |
现在,我将该查询传递给一个接受IEnumerable的函数,并根据LastName进行一些筛选:好的。
1 2 3 4 5 6 7
| public void DoStuff(IEnumerable<Customer> customers)
{
foreach(var cust in from c in customers where c.LastName.StartsWith("Ro"))
{
Console.WriteLine(cust.CustomerId);
}
} |
我们在这里做了第二个查询,但它是在一个IEnumerable上完成的。这里将要发生的是,将对第一个查询进行计算,运行此SQL:好的。
1
| select CustomerId, FirstName, LastName from Customer where FirstName like 'Ad%' |
所以我们要把所有的FirstName的人都从"Ad"开始。注意这里没有关于LastName的内容。这是因为它被过滤掉了客户端。好的。
一旦返回这些结果,程序将迭代这些结果,并只传递其LastName以"Ro"开头的记录。这样做的缺点是,我们带回了数据——也就是说,所有LastName没有以"Ro"开头的行——这些数据可能已经在服务器上被过滤掉了。好的。好啊。
- @AdamRobinson——所以更好的方法是让他们得到一个返回的查询,这个查询仍然没有命中数据库,当他们需要一个列表时,他们可以迭代它(然后它会命中数据库源)。或者最好直接给他们一份球棒的清单?
- @乔博2:这取决于你想做什么,所以没有一个正确的答案。在某些情况下,允许用户进一步细化查询可能是有利的,尤其是在您可能具有多个不同查询所共有的复杂查询逻辑的情况下。虽然这并不是说您应该总是这样做,但是返回带有基本复杂逻辑的IQueryable将允许您在不同的地方重用该逻辑,而不需要在代码中复制它。
- @亚当·罗宾逊-在这种情况下会发生什么。我返回一个IQueryable列表,但我的视图需要使用IEnumerator。我会把它投射到这个上面,你说的那个问题是不是可以枚举的?
- @chobo2:IEnumerator还是IEnumerable?如果您将EDOCX1的一个实例(0)传递给一个接受IEnumerable的函数,那么您就可以了,但是在该函数中所做的所有查询都将在客户端完成(除非它将其强制转换回IQueryable,当然,如果要这样做,它只需要一个IQueryable)。如果它期望IEnumerator,那是完全不同的接口。
- 亚当·罗宾逊-嗯,我没有意识到他们中有两个我认为是不可数的。如果它在客户机端执行所有查询,那就太糟糕了。我要做的是让一个viewModel发送到包含可遍历的IEnumerable的视图。
- @乔博2:很可能是IEnumerable。由于IEnumerator本身只是一个用于枚举的临时对象,所以很少有事情需要它。只是确定一下。至于过滤客户端,一般来说,您希望在服务器上尽可能多地进行过滤。如果可能的话,你要避免带回你将要丢弃的数据。有时是不可能的(如果逻辑不能用SQL表示),有时是禁止的(SQL逻辑很难读取,但是客户机代码很容易),否则这是首选的做法。
- @Chobo2:检查我刚才对答案所做的编辑,看看是否能让它更清晰一点。
- @AdamRobinson不会var query=(来自db.customers中的c,其中c.customerid==5 select c).first();创建类似SQL的select top 1 customerid,…等??
- @德雷扎:是的,会的。
- 你在你的文章中提到过这个事实吗?当我们在查询分析器中捕获查询时,如果存在where子句,那么它将获取准确的记录。不是吗?
- @RBT:这个注释指的是.aseNumerable().where()的用法,它将绕过IQueryable接口。
- 对的。所以在数据库服务器端将没有过滤。那么假设我的表中有ID为1、2、3、4、5、6、7的记录,并且查询有CustomerId=5的条件,那么使用Enumerable(转换为SQL for DB Server)的LINQ查询会将ID为1到5的记录带到客户端还是1到7?
- @RBT:就规范而言,如果没有order by子句,您的数据库服务器就没有义务以任何特定的顺序返回这些行。实际上(对于SQL Server),它们将按聚集索引存储它们的顺序返回。也就是说,只有在这个简单的情况下,您才有一个表作为数据源。如果您做了任何更高级的事情,即加入表或(天不允许)使用Include来急切地加载导航属性,那么您最终会收回更多的内容。
- 我看到了很多答案,我发现这一个更有希望。谢谢。
IQueryable:抽象数据库访问,支持对查询的延迟评估
List:条目的集合。不支持延迟评估
IEnumerator提供迭代和IEnumerable的能力(IQueryable和List都是)
这段代码的问题很简单——它总是在被调用时执行查询。如果您要返回db.User.Where(...)(这是一个IQueryable),您将保留对查询的评估,直到实际需要它为止(重复)。另外,如果该方法的用户需要指定更多的谓词,那么这些谓词也将在数据库中执行,这将使其更快地执行。
- 你还说了什么样的谓词,比如说它们做了它没有命中数据库的where子句?那么,这样如果他们需要说过滤它,甚至更多,他们可以在你对它进行迭代之前不触及数据库吗?
- 一般来说,只有在迭代时,才会对IQueryable进行评估。现在,如果一个要将另一个Where(...)链接到它,那么该操作也将在数据库中执行。使用您的代码,它将在内存中进行计算。数据库被设计用来完成这些任务,并且比内存过滤更快地完成这些任务。
当需要某个实体的强类型集合时,请使用iList或List 。
并且使用Iqueryable和Ienumurator当您想要获得作为对象集合的哑数据时,它将作为松散类型集合返回,并且不应用任何限制。
我宁愿使用List,因为在强类型集合中使用了列表包装和强制转换我的结果集。
此外,使用列表将使您能够添加、排序和将层转换为数组、IEnumurator或可查询。