以下是我的教科书中的一个示例:
var allGenres = from genre in myEntities.Genres.Include("Reviews")
orderby genre.Name
select new { genre.Name, genre.Reviews };
Repeater1.DataSource = allGenres.ToList();
Repeater1.DataBind();
书上写着:
,只要您调用ToList(),就会执行查询,并从数据库中检索相关体裁和评论并将其分配给DataSource属性
所以我的问题是,如果我摆脱Repeater1.DataSource = allGenres.ToList();
,var allGenres包含什么?因为查询尚未执行?
答案 0 :(得分:2)
有三个阶段需要理解。
第三,强制查询即时结果。
var allGenres = from genre in myEntities.Genres.Include("Reviews")
orderby genre.Name
select new { genre.Name, genre.Reviews };
在此代码中,仅创建查询。它作为一个人在墓地里死了。 如果需要延迟执行,则可以使用for循环等遍历结果。
要强制立即执行,可以使用
之类的转换运算符ToList, ToArray, ToLookup, and ToDictionary.
希望有帮助。
您可以在此行上放置一个断点:
var allGenres = from genre in myEntities.Genres.Include("Reviews")
orderby genre.Name
select new { genre.Name, genre.Reviews };
,请注意没有任何反应。在以下行之后, voila SQL事件探查器将显示发生的sql查询:
Repeater1.DataSource = allGenres.ToList();
答案 1 :(得分:1)
您的对象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的返回值和不返回IQueryable的返回值。第一组是诸如Where
,Select
,Join
,GroupBy
之类的函数。它们都返回某种IQueryable。
只要连接此组的功能,表达式就会更改。该查询尚未执行。返回值仍然是表示查询能力的对象。这些函数使用延迟执行(或延迟执行),这意味着查询尚未执行。您会认识到这些函数,因为它们返回IQueryable<...>
。这些功能的说明的“备注”部分还提到推迟执行。
仅在您直接或间接使用GetEnumerator()
调用MoveNext()
/ foreach
后,查询才被执行。
如果您开始枚举,则将Expression
发送到Provider
,后者将Expression
转换为查询执行者可以理解的语言(SQL),并命令执行程序来执行查询。提取的数据将转换为IEnumerable<...>
,然后对其进行枚举,就好像该数据是本地数据一样。
这取决于提供者的创建者,但是有时提供者确实很聪明。它不会从您的产品数据库中获取所有数百万个产品,但会获取一个产品页面。当您枚举此页面时,提供程序将获取下一页。这样可以提高处理速度,因为获取的产品不会超过实际使用的数量,而且您可以在获取所有产品之前就开始枚举。
我提到了使用延迟执行的LINQ函数(=返回IQueryable)。另一组函数将执行查询。这组函数包含诸如ToList
,ToDictionary
,Max
,FirstOrDefault
,Count
之类的函数,它们不返回IQueryable<...>
,但有些返回{ {1}}。如果您查看源代码(google为“参考源可查询目录”的谷歌),您会发现他们将通过调用foreach或GetEnumerator来做到这一点。
由于必须将TResult
转换为SQL,因此查询的可能性比Expression
少。例如,您不能在查询中使用任何本地定义的方法。如果这样做,在执行查询后,您将立即获得运行时异常,告诉您无法将表达式转换为SQL。
可以执行哪种表达式取决于必须由谁执行查询。有一个list of Supported and Unsupported LINQ methods for ling-to-entities。
答案 2 :(得分:0)
如果使用的是Entity Framework,则allGeneres将是IQueryable<Genre>
接口的实例。该行为称为延迟执行。有关更多参考,https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution#deferred-query-execution