【问题标题】:Use ToList() in Linq and Exe Time在 Linq 和 Exe 时间中使用 ToList()
【发布时间】:2019-02-13 04:27:06
【问题描述】:

下面是我教科书中的一个例子:

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 包含什么?因为查询没有被执行?

【问题讨论】:

  • allGenres 视为购物清单,将ToList 视为实际去超市购物。 allGenres 知道需要做什么。 ToList 搞定具体来说,它包含一个匿名类型的IQueryable
  • allGenres 将包含表达式。如果您不对它调用ToList 或对其进行迭代,它甚至永远不会访问您的数据库来检索结果。我的猜测是,即使您在执行DataBind 之前没有调用ToList,它很可能仍然有效,因为Repeater 很可能会遍历DataSource,尽管这可能会在以后导致性能问题

标签: c# asp.net linq


【解决方案1】:

需要理解三个阶段。

  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 Profiler 将显示正在发生的 sql 查询:

Repeater1.DataSource = allGenres.ToList();

【讨论】:

    【解决方案2】:

    您的对象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&lt;...&gt; 看起来与IEnumerable&lt;...&gt; 非常相似。不同之处在于它通常意味着由不同的进程处理,例如数据库管理系统或不同计算机上的服务器,它也可以表示 CSV 文件中的行或其他任何内容。 IQueryable 的目的是将获取数据的方式与对获取的数据的操作分开。

    就像 IEnumerable 拥有枚举能力IQueryable 拥有查询数据的能力

    为此,IQueryable 有一个 Expression 和一个 ProviderExpression 以通用方式定义必须获取 什么 数据。 Provider 知道必须提供数据(数据库),以及该数据提供者需要什么语言 (SQL)。

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

    只要你连接这个组的函数,表达式就会改变。查询尚未执行。返回值仍然是一个代表查询能力的对象。这些函数使用延迟执行(或延迟执行),这意味着查询尚未执行。您会认出这些函数,因为它们返回 IQueryable&lt;...&gt;。这些函数描述的备注部分也提到了延迟执行。

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

    如果您开始枚举,Expression 将发送给ProviderProvider 会将Expression 翻译成查询执行者可以理解的语言 (SQL),并命令执行者执行查询。获取的数据将转换为 IEnumerable&lt;...&gt;,然后将其枚举为好像数据是本地数据一样。

    这有点取决于谁制作了 Provider,但有时 Provider 真的很聪明。它不会从您的产品数据库中获取所有数以百万计的产品,但它会获取产品页面。当您枚举此页面时,Provider 会获取下一页。这将提高处理速度,因为获取的产品不会比您实际使用的多得多,而且您可以在获取所有产品之前开始枚举。

    我提到了使用延迟执行(= 返回 IQueryable)的 LINQ 函数。另一组函数将执行查询。这组函数包含ToListToDictionaryMaxFirstOrDefaultCount等函数,它们不返回IQueryable&lt;...&gt;,但有一些TResult。如果您查看源代码(google for "reference source queryable tolist"),您会发现他们将通过调用 foreach 或 GetEnumerator 来完成此操作。

    因为必须将Expression 转换为SQL,所以查询的可能性小于IEnumerable&lt;...&gt;。例如,您不能在查询中使用任何本地定义的方法。如果你这样做了,一旦执行查询,你就会得到一个运行时异常,告诉你表达式不能被翻译成 SQL。

    可以执行什么样的表达式取决于谁必须执行您的查询。有一个list of Supported and Unsupported LINQ methods for ling-to-entities

    【讨论】:

      【解决方案3】:

      如果您使用实体框架,allGeneres 将是IQueryable&lt;Genre&gt; 接口的实例。该行为称为延迟执行。更多参考https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution#deferred-query-execution

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-21
        • 1970-01-01
        • 2020-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多