繁体   English   中英

为什么实体框架dbSet.Include需要这么长时间才能返回?

[英]Why is Entity Framework dbSet.Include taking so long to return?

我在表上做一个简单的“获取”,例程的“包含”部分花费的时间比我期望的要长得多。

我将性能问题缩小到以下代码段:

private List<Task> GetFilteredTasksWithOptionalIncludes(EntityRepository<Task> repo, ITaskRequest model, TaskIncludesModel includes, string accountID)
{
    var includedEntities = new List<Expression<Func<Task, object>>>();

    includedEntities.Add(t => t.Document.Transaction.Account);

    includedEntities.Add(p => p.Signature);

    if (includes != null)
    {
        if (includes.IncludeWorkflowActions)
        {
            includedEntities.Add(p => p.Actions);
        }

        if (includes.IncludeFileAttachments)
        {
            includedEntities.Add(p => p.Attachments);
        }
    }

    IQueryable<Task> tasks = repo.GetAllIncluding(includedEntities.ToArray());  //RETURNS SLOW

    return tasks.ToList();  //RETURNS FAST
}

我的所有代码运行都非常快,直到达到repo.GetAllInclude方法,如下所示:

public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties)
{
    foreach (var includeProperty in includeProperties)
    {
        dbSet.Include(includeProperty).Load();
    }

    return dbSet;
}

当我逐步执行代码时,GetAllIncluded()行最多需要6或7秒的时间才能返回,但task.ToList()返回的时间不到一秒钟(这是实际的SQL查询运行时,因此这让我感到惊讶)。

当我注释掉加载include的foreach循环时,整个调用在不到一秒的时间内返回。 是什么原因导致包含内容花费这么长时间? 还有更好的方法吗?

如果有帮助,这里是调用周围的SQL事件探查器。 红线上方的所有内容均来自GetAllIncluded调用。 红线下方的所有内容都是对数据的实际查询。 有没有更有效的方法可以做到这一点? 看起来很简单的通话似乎不需要10秒。 在此处输入图片说明

当您在该循环中调用.Load()时,实际上是在访问数据库并将数据放入上下文中。

因此,根据您要执行该循环的频率,您会不断地运行该查询。

我建议删除.Load()但您仍然可以保留.Load()函数。 通用存储库的基本“包含”功能如下所示:

public IQueryable<TEntity> Including(params Expression<Func<TEntity, object>>[] _includeProperties)
    {
        IQueryable<TEntity> query = context.Set<TEntity>();
        return _includeProperties.Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
    }

一旦调用了此方法并掌握了IQueryable ,只需对其调用.ToList()即可仅从SQL中提取一次。

阅读http://msdn.microsoft.com/en-gb/data/jj592911.aspx,以了解Load方法的实际作用。

根据您的评论进行编辑:

您可以实现我发布的上述功能,并以与您现在正在使用的方式类似的方式使用它,然后在需要时可查询的任务上隐式调用Load()

private List<Task> GetFilteredTasksWithOptionalIncludes(EntityRepository<Task> repo, ITaskRequest model, TaskIncludesModel includes, string accountID)
{
    var includedEntities = new List<Expression<Func<Task, object>>>();

    includedEntities.Add(t => t.Document.Transaction.Account);

    includedEntities.Add(p => p.Signature);

    if (includes != null)
    {
        if (includes.IncludeWorkflowActions)
        {
            includedEntities.Add(p => p.Actions);
        }

        if (includes.IncludeFileAttachments)
        {
            includedEntities.Add(p => p.Attachments);
        }
    }

    IQueryable<Task> tasks = repo.Including(includedEntities.ToArray());  
    tasks.Load();
    return tasks.ToList();  
}

或者作为Thewads的后续步骤,只需编写一个很好的旧sproc以一次将所有这些返回多个集合中即可,然后可以使用适当的索引优化数据库架构,并使其尽快运行。

那可能不会流行,但是当您开始谈论数据库性能时,它是一种更快的方法(并且更容易使用,因为您拥有所有用于性能调整的sql工具)...例如,打开查询执行计划。 您可能会发现发疯了。

另一个问题是……您真的需要所有这些数据吗,在加载所有数据之前是否没有可以应用的过滤条件? (假设您解决了多次加载问题)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM