【问题标题】:Simple DbSet<TEntity>.Find() Call Taking Forever简单的 DbSet<TEntity>.Find() 调用永远
【发布时间】:2013-05-02 18:24:02
【问题描述】:

我从 DbSet.Find() 调用中获得了糟糕的性能。我的代码如下所示:

public class Area
{
    [Key]
    public string Id { get; protected set; }

    // ...
}

public class MyContext : DbContext
{
    //...
    public DbSet<Area> Areas { get; set; }
    //...
}

// This is the call that takes so long
Area area = context.Areas.Find(id);    

我知道这必须搜索实体集,检查更改跟踪等,并将触发对数据库的调用。问题是它比简单的context.Areas.SingleOrDefault(x =&gt; x.Id == id) 调用要长几个数量级。比我认为合理的要多得多。根据另一个问题的提示,我还尝试暂时关闭更改跟踪但没有成功(它似乎没有任何效果):

try
{
    context.Configuration.AutoDetectChangesEnabled = false;
    return context.Areas.Find(id);
}
finally
{
    context.Configuration.AutoDetectChangesEnabled = true;
}

为了尝试找出问题的根源,我启动了我的分析器。这是我发现的:

看起来它一直在花时间准备执行计划。但是为什么在.Find() 调用期间需要这么长时间而不是显式的.SingleOrDefault 调用(请注意,在调用堆栈的顶部附近,它实际上正在准备SingleOrDefault 调用)。有什么方法可以查看.Find() 方法实际上正在尝试编译的查询?

【问题讨论】:

  • 你的 Area 类的完整代码吗? ti 是从其他类继承的吗?
  • @jure - 几乎。显然还有一些其他属性和几种方法,但大多数情况下它是一个非常简单的实体类。
  • 您展示的图片仅占查询的 6%(我假设所有这 6% 的总和超过 100 个)。 Area 是否引用了许多其他实体?延迟加载是否被禁用,因此是否加载所有关联?问题可能是您的操作正在加载一个 large 相关的对象图。
  • @qujck - 6% 超过了应用程序被分析的总时间,其中包括另外 94% 的其他活动。关于配置文件屏幕截图需要注意的重要事项是 1) 顶部的 Find() 在绝对时间方面花费了太长的时间,以及 2) Find() 调用中花费的大部分时间都在 Compile( ) 进一步调用堆栈。关于您的问题 - 该实体没有任何参考资料。有可能 Find() 调用也试图加载一些其他内容 - 我将尝试处理它最终发送的确切查询。

标签: c# .net entity-framework linq-to-entities


【解决方案1】:

我从来没有弄清楚为什么这需要这么长时间。它生成的查询看起来很合理(只是一个简单的SELECT)。我正在使用一个非常复杂的域模型,虽然上面描述的 Area 实体是孤立和简单的,但也许 Entity Framework 正在以某种方式尝试构建视图或生成触及域模型其他部分的查询(或者更确切地说,试图决定它不应该)。

无论如何,我确实制定了解决方法。诀窍是手动完成(我认为)Find() 调用最初所做的工作:

public Area FindArea(string id)
{
  // First see if the DbSet already has it in the local cache
  Area area = context.Areas.Local.SingleOrDefault(x => x.Id == id);

  // Then query the database
  return area ?? context.Areas.SingleOrDefault(x => x.Id == id);
}

使用扩展方法可以很容易地推广这种技术。此特定实现假定实体将其 ID 存储在字符串 Id 列中并实现以下接口。但是,通过一些调整,您可以使其适应各种领域模型。

public interface IEntityWithId
{
  string Id { get; }
}

public static object FastFind<TEntity>(this DbSet<TEntity> dbSet, string id)
  where TEntity : IEntityWithId, class
{
  // First see if the DbSet already has it in the local cache
  TEntity entity = dbSet.Local.SingleOrDefault(x => x.Id == id);

  // Then query the database
  return entity ?? dbSet.SingleOrDefault(x => x.Id == id);
}      

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 2011-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-01
  • 1970-01-01
相关资源
最近更新 更多