您的对象allGenres 是一个实现IQueryable<...> 的对象。这意味着它表示一个序列,并且它具有获取序列的第一个元素的功能,一旦你得到了元素,你就可以获取下一个元素,直到没有更多的元素。
<...> 部分定义了序列中的元素类型。所以IQueryable<Book>表示可以查询Books的序列,可以一个接一个的枚举。序列的每个元素都是Book类的对象
这个枚举是使用 IQueryable 的基本接口提供的,即IEnumerable<Book>。
在最低级别,此枚举按如下方式完成:
IEnumerable<Book> books = ... // for example new List<Book>(), or new Book[10]
IEnumerator<Book> bookEnumerator = books.Getenumerator();
// as long as there are books, print the title:
// the first MoveNext() moves to the first element
// every other MoveNext() move to the next element
// it returns false if there is no such element (no first, or no next)
while (bookEnumerator.MoveNext())
{
// the enumerator points to the next element. Property Current contains this element
Book book = bookEnumerator.Current;
Console.WriteLine(book.Title);
}
通常我们不会使用这种低级功能。你会更频繁地看到 foreach 变量:
foreach (Book book in books)
{
Console.WriteLine(book.Title);
}
foreach 将为您执行 GetEnumerator() / MoveNext() / Current
注意IEnumerable 不代表枚举本身,它代表枚举能力。很多时候人们并不那么精确,并将 IEnumerable 称为序列本身。但请记住:要访问序列的元素,您需要对它们进行枚举(使用 MoveNext 或调用 foreach)。
IQueryable<...> 看起来与IEnumerable<...> 非常相似。不同之处在于它通常意味着由不同的进程处理,例如数据库管理系统或不同计算机上的服务器,它也可以表示 CSV 文件中的行或其他任何内容。 IQueryable 的目的是将获取数据的方式与对获取的数据的操作分开。
就像 IEnumerable 拥有枚举能力,IQueryable 拥有查询数据的能力。
为此,IQueryable 有一个 Expression 和一个 Provider。 Expression 以通用方式定义必须获取 什么 数据。 Provider 知道谁必须提供数据(数据库),以及该数据提供者需要什么语言 (SQL)。
也许您已经注意到有两种 LINQ 语句。返回 IQueryable 的和不返回的。第一组是Where、Select、Join、GroupBy 等函数。它们都返回某种 IQueryable。
只要你连接这个组的函数,表达式就会改变。查询尚未执行。返回值仍然是一个代表查询能力的对象。这些函数使用延迟执行(或延迟执行),这意味着查询尚未执行。您会认出这些函数,因为它们返回 IQueryable<...>。这些函数描述的备注部分也提到了延迟执行。
只有在您直接或间接使用foreach 调用GetEnumerator() / MoveNext() 后,才会执行查询。
如果您开始枚举,Expression 将发送给Provider,Provider 会将Expression 翻译成查询执行者可以理解的语言 (SQL),并命令执行者执行查询。获取的数据将转换为 IEnumerable<...>,然后将其枚举为好像数据是本地数据一样。
这有点取决于谁制作了 Provider,但有时 Provider 真的很聪明。它不会从您的产品数据库中获取所有数以百万计的产品,但它会获取产品页面。当您枚举此页面时,Provider 会获取下一页。这将提高处理速度,因为获取的产品不会比您实际使用的多得多,而且您可以在获取所有产品之前开始枚举。
我提到了使用延迟执行(= 返回 IQueryable)的 LINQ 函数。另一组函数将执行查询。这组函数包含ToList、ToDictionary、Max、FirstOrDefault、Count等函数,它们不返回IQueryable<...>,但有一些TResult。如果您查看源代码(google for "reference source queryable tolist"),您会发现他们将通过调用 foreach 或 GetEnumerator 来完成此操作。
因为必须将Expression 转换为SQL,所以查询的可能性小于IEnumerable<...>。例如,您不能在查询中使用任何本地定义的方法。如果你这样做了,一旦执行查询,你就会得到一个运行时异常,告诉你表达式不能被翻译成 SQL。
可以执行什么样的表达式取决于谁必须执行您的查询。有一个list of Supported and Unsupported LINQ methods for ling-to-entities。