在Linq和Exe Time中使用ToList()

时间:2019-02-13 04:27:06

标签: c# asp.net linq

以下是我的教科书中的一个示例:

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包含什么?因为查询尚未执行?

3 个答案:

答案 0 :(得分:2)

有三个阶段需要理解。

  1. 首先,创建查询时。
  2. 第二,当迭代查询变量(延迟执行)时。
  3. 第三,强制查询即时结果。

    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和一个ProviderExpression以通用方式定义必须提取的数据什么Provider知道必须提供数据(数据库)以及该数据提供者需要哪种语言(SQL)。

也许您已经注意到有两种LINQ语句。返回IQueryable的返回值和不返回IQueryable的返回值。第一组是诸如WhereSelectJoinGroupBy之类的函数。它们都返回某种IQueryable。

只要连接此组的功能,表达式就会更改。该查询尚未执行。返回值仍然是表示查询能力的对象。这些函数使用延迟执行(或延迟执行),这意味着查询尚未执行。您会认识到这些函数,因为它们返回IQueryable<...>。这些功能的说明的“备注”部分还提到推迟执行。

仅在您直接或间接使用GetEnumerator()调用MoveNext() / foreach后,查询才被执行。

如果您开始枚举,则将Expression发送到Provider,后者将Expression转换为查询执行者可以理解的语言(SQL),并命令执行程序来执行查询。提取的数据将转换为IEnumerable<...>,然后对其进行枚举,就好像该数据是本地数据一样。

这取决于提供者的创建者,但是有时提供者确实很聪明。它不会从您的产品数据库中获取所有数百万个产品,但会获取一个产品页面。当您枚举此页面时,提供程序将获取下一页。这样可以提高处理速度,因为获取的产品不会超过实际使用的数量,而且您可以在获取所有产品之前就开始枚举。

我提到了使用延迟执行的LINQ函数(=返回IQueryable)。另一组函数将执行查询。这组函数包含诸如ToListToDictionaryMaxFirstOrDefaultCount之类的函数,它们不返回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