【问题标题】:In .NET Core EF, why Query run so much faster if .Select() is used than ToListAsync()? [duplicate]在 .NET Core EF 中,如果使用 .Select() 比 ToListAsync() 运行,为什么 Query 运行得这么快? [复制]
【发布时间】:2020-11-24 14:33:27
【问题描述】:

为什么使用 Select() 比 ToListAsync() 从数据库中检索数据要快得多?我在一个表中有 5000 个虚拟条目。

我的代码:

private IQueryable<TEntity> GetQueryable<TEntity>(
            Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            int? skip = null, int? take = null, bool asNoTracking = false,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null)

            where TEntity : class, IEntity
        {
            IQueryable<TEntity> query = _context.Set<TEntity>();
                
            if (filter != null)
                query = query.Where(filter);

            if (include != null)
                query = include(query);

            if (orderBy != null)
                query = orderBy(query);

            if (skip.HasValue)
                query = query.Skip(skip.Value);

            if (take.HasValue)
                query = query.Take(take.Value);

            if (asNoTracking)
                query = query.AsNoTracking();

            return query;
        }

当这样使用方法时:

public virtual async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            int? skip = null, int? take = null, bool asNoTracking = false,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null)
            where TEntity : class, IEntity
        {
            return await GetQueryable<TEntity>(null, orderBy, skip, take, asNoTracking, include).ToListAsync();
        }

这需要 30 秒!

但是当使用这段代码时:

public virtual async Task<IQueryable<dynamic>> GetDynamicAsync<TEntity>(
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            int? skip = null, int? take = null, bool asNoTracking = false,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
            Expression<Func<TEntity, dynamic>> columns = null)
            where TEntity : class, IEntity
        {
            return GetQueryable<TEntity>(null, orderBy, skip, take, asNoTracking, include).Select(columns);
        }

这甚至不需要一秒钟!我想最多 1 秒。

这是为什么呢?有人可以解释吗?即使我在“columns”变量中添加了所有表的列,检索起来仍然非常快。那么这两个查询有什么区别呢?如果这里没有使用 await 也可以吗?我不能使用 await,因为它说 IQueryable 没有 GetAwaiter。这让我很担心。

谢谢!

编辑:添加服务代码

public async Task<ManyResult<LibrariesForTableDTO>> GetLibrariesForTable(DatatableSearchDTO dto)
        {
            var response = new ManyResult<LibrariesForTableDTO>();
            try
            {
                //var libraries = await _repo.GetDynamicAsync<Library>(columns: x => new { x.Id, x.Name, x.Description });
                var libraries = await _repo.GetAllAsync<Library>();
                response.Entities = _mapper.Map<List<LibrariesForTableDTO>>(libraries);
                response.TotalFilteredEntities = libraries.Count();
                response.TotalEntities = libraries.Count();
                response.Success = true;
            }
            catch (Exception e)
            {
                response.Success = false;
                response.Message = "Error retrieviing the libraries.";
                _logger.LogError("Error calling GetLibrariesForTable: {0} - {1}", e.Message, e.StackTrace);
            }

            return response;
        }

【问题讨论】:

  • 秒只返回普通的 IQueryable ...显然它根本不查询数据库
  • 我不明白。我正在检索正确的数据吗?那么我检索数据的方式是否错误?
  • 您返回的是不同的东西。您的第一种方法(速度较慢的方法)返回数据。第二个(快速)返回一个可以获取数据的函数。如果您将数据打印到控制台,您会发现它们运行的​​时间大致相同,因为两者都需要实际获取数据。
  • Select 正在返回购物清单 - 操作步骤。 ToList(或Select 的任何枚举)正在购物。写购物清单很快。去商店买东西很慢。
  • 我编辑了我的帖子,我添加了调用查询方法的服务代码。注释的“库”返回数据列表。并且这个映射仍然只需要一秒钟。第二个(未注释)“库”需要 30 秒。你们能告诉我这里的“获取数据”是什么部分吗?因为我真的不明白它什么时候发生。我现在很困惑。谢谢!

标签: c# entity-framework asp.net-core


【解决方案1】:

IQueryable 上运行Select 不会执行查询,这就是它快速的原因。 当你运行ToListFirstOrDefault 之类的东西时,它实际上需要枚举结果,这就是查询执行的时候。 它本质上只是向可查询对象添加一个投影,以便在查询执行时,可以将该投影转换为 SQL 查询等。

因此,如果您不在那里执行查询,则会延迟查询执行以供以后枚举 IQueryable 时使用。

【讨论】:

  • 但我不明白,先生。如果它不执行查询,为什么我要从数据库中获取数据?带有 Select 的 IQueryable 确实返回了我想要的数据。这是为什么呢?
  • 你怎么看的?例如,如果您使用循环遍历它,它将在那时运行查询。您用于查看 IQueryable 数据的任何工具都会枚举它,从而导致查询运行。
  • 我编辑了我的帖子先生,我添加了调用查询方法的服务代码。注释的“库”返回数据列表。并且这个映射仍然只需要一秒钟。第二个(未注释)“库”需要 30 秒。你能告诉我这里的哪一部分是“枚举”还是真正的“执行”?因为我真的不明白它什么时候发生。我现在很困惑。谢谢!
  • Linq - 延迟执行。这里有一个解释medium.com/exam-70-487/…
  • 当您在映射器上调用 Map 时,它将运行查询。对 Count() 的两次调用也将运行查询以获取结果计数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 1970-01-01
  • 2019-11-01
  • 2013-05-04
  • 2012-09-13
  • 2013-05-09
相关资源
最近更新 更多