c# - 为什么 Entity Framework dbSet.Include 需要这么长时间才能返回?

标签 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 秒,但 tasks.ToList() 返回所需的时间不到一秒(这是实际 SQL 查询时得到运行所以这让我感到惊讶)。

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

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

最佳答案

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

关于c# - 为什么 Entity Framework dbSet.Include 需要这么长时间才能返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23409148/

相关文章:

c# - 返回 SqlDataReader

c# - 将现有数据库链接到 Entity Framework

c# - 设置EF拦截器文件名布局

c# - 在 C# 中的给定变量之间的表中查找最小值和最大值

javascript - 单击几次后 ModalPopupExtender 不显示

c# - 如何解析包含引号的字符串

c# - 无法使用 {} 将事件添加到动态生成的控件

c# - 如何在 Windows 操作系统上使用 C# 搜索文件

sql - Entity Framework 6.1 - 使用 INCLUDE 语句创建索引

c# - 使用 EF 的 MVC 3 图像