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;
}

为了试图弄清楚这一点,我启动了我的分析器。这是我发现的:

痕迹

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

4

1 回答 1

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);
}      
于 2013-05-15T12:53:13.587 回答