【问题标题】:Entity Framework, linq functions and memory usage实体框架、linq 函数和内存使用
【发布时间】:2017-01-26 14:36:48
【问题描述】:

我是 EF 新手,我曾经使用数据集、表适配器和存储过程。我刚刚发现了 EF 的简单性,我发现 EF 方法对我的发展有很大帮助。我有几个问题,我试图寻找他们的答案,但徒劳无功。由于我总是与拥有 巨大表 的客户合作,因此我打这个电话的事实例如:

_ordersContext.Services.ToList()

这是否意味着整个 Services 表已加载到内存中?如果答案是肯定的(顺便说一句,我认为答案是肯定的),我们可以通过使用 linq 函数来避免内存成本吗?例如 Take() 方法? (我的意思是,如果您只想拥有 10 条记录,不将整个表加载到内存中)。关于其他 linq 函数的相同问题,例如 where、first、firstordefault、count 等……我的意思是,我们必须加载整个表吗? EF 在最佳实践和内存使用方面。

【问题讨论】:

    标签: c# entity-framework linq


    【解决方案1】:

    在每个LINQ method 上查看 MSDN。如果你找到了deferred这个词,你就知道这个方法不执行查询并且可以与其他方法链接。只有那些不使用延迟执行的人才会开始处理查询并将结果加载到内存中。

    请记住,您可以强制 Linq-To-Objects 而不用 AsEnumerable() 将所有内容加载到内存中。这会将您的查询转换为 sql,执行数据库查询并将结果流式传输到内存中。

    所以你可以这样做:

    var orderList = _ordersContext.Services
        .Where(x => somecondition)
        .OrderBy(x => x.Column)
        .AsEnumerable() // after this you can use any .NET method since it doesnt need to be translated to sql
        .Where(x => complex filter not supported by Linq-To-Entities)
        .Take(10)
        .ToList()
    

    这仍然只会将 10 条记录加载到内存中,并且它使用数据库引擎来(预)过滤或排序,但允许使用 Linq-To-Entities 不支持的 .NET 方法。

    相关:

    Linq - What is the quickest way to find out deferred execution or not?

    通常返回序列的方法使用延迟执行和 返回单个对象的方法不会。

    例外是返回集合的方法,如 ToListToArrayToLookupToDictionary,它们不使用延迟执行。

    【讨论】:

    • This will still only load ten records into memory。如果我不知道更好并且我读到了,我会假设你说它会发出Select Top 10 ...
    • @sgmoore:不,AsEnumerable 将类型强制转换为 IEnumerable<T>,这会强制 Linq-To-Objects。因此,在此之前的所有内容都将转换为 sql 查询,并将结果流式传输到内存中。因此,在 ADO.NET 中,它就像一个带有 SqlDataReaderwhile(reader.Read(){} 循环的查询(包括 WHERE 和 ORDER BY),带有一个计数器,如果处理了 10 条记录,它将打破这个循环。
    • AsEnumerable 只是将 IQueryable<T> 转换为 IEnumerable<T>,但这会强制 Linq-To-Objects,因为将使用 Enumerable- 而不是 Queryable 方法。
    • 我试图说明的一点是,这会要求 Sql Server 为您提供大量记录,一旦 Sql 准备好它们并开始将它们发送给您,您将在获得前十个记录后停止它。有时这是不可避免的,但我认为 AsEnumerable 应该始终带有警告。
    • @sgmoore: 当然,这就是为什么我的伪查询在使用AsEnumerable 之前使用WhereOrderBy。这只是一种启用所有不能在 Linq-To-Entities/Linq-To-Sql 中使用的代码并避免调用ToList(很多人这样做)的方法。这确实会在开始处理之前将所有内容加载到内存中。所以db.LargeTable.AsEnumerable().Take(1)db.LargeTable.ToList().Take(1) 好很多。后者可能导致OutOfMemoryExceptions。 (当然在这种情况下也不需要AsEnumerable
    【解决方案2】:

    是的。整个表将被加载到内存中。您对 ToList() 的调用将立即执行查询,在这种情况下是整个表。

    ToList(), ToArray(),... 函数将返回列表。

    FirstOrDefault 最多返回一个项目

    SingleOrDefault 最多返回一项,如果结果多于一项,则抛出异常。

    关于 ef、linq 和延迟执行的完整说明请参见此处

    https://blogs.msdn.microsoft.com/charlie/2007/12/10/linq-and-deferred-execution/

    【讨论】:

      【解决方案3】:

      在调用 ToList() 之前没有加载任何服务,但是第一次调用 ToList() 时,EF 将加载整个服务,因此为避免加载所有服务,请先查询服务以获取您需要的内容,在服务上调用 Skip、Take、Where 或任何内容,EF 将生成一个查询,该查询会加载您需要获取的确切服务

      【讨论】:

        【解决方案4】:

        关于您的第一个问题,是的,这会将所有记录从数据库加载到客户端。 在处理包含大量记录的表时,正如您所提到的,您可以使用 .Skip(() => ...)Take(() => ....)

        关于内存和其他 linq epxresions,如 .Where、SingleOrDefault、FirstOrDefault 等......从查询表达式到 SQL 查询的转换存在开销,但 EF 已经缓存了这些并在它重用和解析查询时能够。所以还不错。

        A关于查询表达式以及您可以查看的更多信息http://www.tutorialsteacher.com/linq/linq-tutorials

        这里有更多关于 EF 的信息http://www.entityframeworktutorial.net/

        关于 EF 查询性能

        EF 将缓存查询并重用它们,正如我已经提到的。

        还创建存储过程并使用它们而不是 RAW sql 查询。这可以通过 SQL Server Profiler 轻松观察到

        关于数据库上下文性能 DbContext 的好处之一是 ChangeTracker,它将在其生命周期内跟踪任何实体更改(直到被释放),如果您计划对特定实体进行一些修改/更新,但是当您进行“只读”查询时,这是可以的你可以稍微优化一下上下文。

        DbContext 具有具有以下设置的 .Configuration 属性。

        AutoDetectChangesEnabled 启用/禁用更改跟踪

        LazyLoadingEnabled 启用/禁用导航属性的延迟加载。 更多关于延迟加载的信息:http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx

        ProxyCreationEnabled 启用/禁用为实体创建运行时包装器。这与实体中的导航属性有关。不久,当您在访问启用了 LazyLoading 的 .Firends 属性时具有一对多关系(假设有很多朋友的人)时,EF 会将其转换为 JOIN 查询,并将获取并实现当前实体的所有朋友,这是可能的,因为覆盖 .Friends 属性行为的代理类。

        因此,当您只执行只读查询时,您可以禁用这些设置。请记住,禁用 LazyLoading 将导致导航属性未实现。要解决此问题,您必须在原始查询中加入/包含这些内容。

        这可以使用此处描述的一些方法来实现:https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

        关于查询实现 您已经发现了两种可能的方法。 使用 .ToList() 或使用 Skip 和 Take 进行分页

        您也可以foreach 将创建SQL CURSOR的查询。

        关于查询具体化的更多信息: https://msdn.microsoft.com/en-us/library/bb738633(v=vs.110).aspx

        这是一些总体提示。此外,您可以查看帖子中的链接,我认为它们是一个很好的起点。

        总结: 根据我的经验,ORM 非常适合新项目,因为它们易于修改,并且可以让更多团队成员接触(更接近 C# 然后 SQL)。但最终部分查询用 SQL 重写,主要是因为从服务器运行 sp 仍然更快,而且 EF 有时会创建复杂的查询,最好避免。

        【讨论】:

          【解决方案5】:

          是的,如果您调用_ordersContext.Services.ToList(),它将加载整个表,是的,您可以使用 LINQ 方法查询数据库并仅检索您需要的记录。

          看看 MSDN 上的这个链接:https://msdn.microsoft.com/en-us/library/bb399367(v=vs.110).aspx

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-07-07
            • 2018-10-29
            • 1970-01-01
            • 2018-11-30
            • 1970-01-01
            • 2011-08-29
            • 1970-01-01
            相关资源
            最近更新 更多