简单的DbSet<TEntity>.Find()调用需要永远等待

3

我从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 => x.Id == id) 调用花费了更多数量级的时间,远超出我认为合理的范围。根据另一个问题的提示,我也尝试过暂时关闭更改跟踪,但没有成功(似乎没有产生任何效果):

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

为了弄清这个问题的原因,我启动了我的分析器。这是我发现的: trace 看起来它在准备执行计划上花费了大量时间。但为什么在 .Find() 调用期间会花费如此之长的时间,而不是在显式 .SingleOrDefault 调用期间(请注意,在调用堆栈的顶部附近,它实际上正在准备一个 SingleOrDefault 调用)?有没有办法看到 .Find() 方法实际上正在尝试编译的查询?

这是你的Area类的完整代码吗?它继承自其他类吗? - Jurica Smircic
@jure - 差不多。显然还有一些其他属性和几个方法,但总体而言,这是一个相当简单的实体类。 - daveaglick
你展示的图片只占查询的6%(我假设所有这些6%加起来超过100)。Area是否引用了许多其他实体?懒加载是否已禁用,因此正在加载所有关联?问题可能是您的操作正在加载大量相关对象图。 - qujck
@qujck - 6%是应用程序被分析的总时间,其中包括其他94%的活动。关于配置文件屏幕截图需要注意的重要事项是:1)顶部的Find()在绝对时间上花费太长时间;2)在Find()调用中花费的大部分时间在堆栈下面的Compile()调用中。关于您的问题-实体没有任何引用。可能的情况是,Find()调用也尝试加载其他一些内容-我将尝试掌握它最终发送的确切查询。 - daveaglick
1个回答

1
我从未弄清楚为什么会这么慢。它生成的查询看起来合理(只是一个简单的SELECT)。我使用了一个相当复杂的领域模型,虽然上面描述的区域实体是隔离和简单的,但也许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);
}      

真的非常奇怪,这样的解决方法竟然解决了你的问题。如果可以在一个简单的示例模型中重现该问题,并且你有时间,你可以将你的问题报告到http://entityframework.codeplex.com/workitem/list/advanced作为可能的错误。 - Slauma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接