Simple DbSet<TEntity>.Find() Call Taking Forever

I am getting terrible performance from a DbSet.Find() call. My code looks like this:

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

I understand that this has to search through the entity set, check change tracking, etc. and will trigger a call to the database. The problem is that it's taking orders of maginitude longer than a simple context.Areas.SingleOrDefault(x => x.Id == id) call. Much more than I would think reasonable. Following a tip on another question, I also tried temporarily turning off change tracking without success (it doesn't appear to have had any effect):

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

To try and get to the bottom of this, I fired up my profiler. This is what I found:

It looks like it's taking all the time preparing an execution plan. But why would this take so long during the .Find() call and not the explicit .SingleOrDefault call (notice that up near the top of the call stack, it actually is preparing a SingleOrDefault call). Is there any way to see the query that the .Find() method is actually trying to compile?

Answers


I never did figure out why exactly this was taking so long. The query it generated looked reasonable (just a simple SELECT). I'm using a pretty complicated domain model, and while the Area entity described above is isolated and simple, perhaps Entity Framework is somehow trying to build views or generate a query that touches other parts of the domain model (or rather, trying to decide that it shouldn't).

In any case, I did formulate a work around. The trick is to manually do the work that (I thought) the Find() call was doing in the first place:

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

This technique could be easily generalized using an extension method. This particular implementation assumes that the entity stores it's ID in a string Id column and implements the following interface. However, with some tweaking you could adapt it to all kinds of domain models.

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

Need Your Help

How to get size of jQuery object

jquery object size

I'm saving key-value pairs in div container by using:

FBAccessTokenData EXC_BAD_ACCESS when refreshing access token

ios facebook sdk crash

I've started to receive crash logs through crashlytics after updating Facebook SDK from 3.5.3 to 3.9 in my app about an EXEC_BAD_ACCESS crash in FBAccessTokenData.m line 126

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.