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

时间:2014-05-01 13:55:40

标签: c# entity-framework

我正在做一个简单的"得到"在桌子上,"包括"例程的一部分比我预期的要长得多。

我已将性能问题缩小到这段代码:

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查询时)得到运行,这让我感到惊讶。)

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

Here is the SQL Profiler around the call if it helps.红线以上的所有内容均来自GetAllIncluded调用。红线下面的所有内容都是数据的实际查询。有没有更有效的方法来做到这一点?对于一个相当简单的电话,似乎不需要10秒钟。 enter image description here

2 个答案:

答案 0 :(得分:1)

当您在该循环中调用.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();  
}

答案 1 :(得分:-1)

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

这可能不会受欢迎但是当你开始讨论数据库性能时它是更快的方式(并且更容易使用,因为你有所有用于性能调优的sql工具)...例如打开查询执行计划。你可能会发现疯狂的事情正在发生。

另一个问题是......你真的需要所有这个数据是否在你加载之前没有可以应用的过滤条件? (假设您修复了多次加载问题)