Query performance: Client evaluation for tracked queries returning an entity by key
See original GitHub issueCurrently a query like this will always cause a database round trip:
var product = context.Products.FirstOrDefault(p => p.Id == pid);
As mentioned in the comments of #797 we have been talking for some time about handling such queries differently: we can check first if an entity exists in the context that matches the predicate, and if found we can short-circuit the database query.
In terms of previous versions of EF this would be equivalent or very similar to DbSet<T>.Find()
and ObjectContext.GetObjectByKey()` (the main difference between these two is that the latter cannot be used to retrieve objects in the added state).
Initially we could support only queries that:
- Return a tracked entity
- Do not use
Include()
- Are restricted by a simple predicate of the general form
entityKey == value
- Have that predicate passed to
First()
,FirstOrDefault()
,Single()
orSingleOrDefault()
Then (also initially or in the longer term) we can pick other scenarios in which the database query can be unnecessary, e.g.:
// Deferred query that return a single entity is compelling as many customers will use it
var product1 = context.Products.Where(p => p.Id == pid).First();
// Composite predicates
var product2 = context.Products.First(p => p.Id == pid && !string.IsNullOrEmpty(p.Name));
// Any() over a predicate by key could sometimes return true without going to the database
var found = context.Products.Any(p => p.Id == pid);
// Contradictions don't need round tripping, but are less common
var never = context.Products.Where(p => p.Presentation != p.Presentation);
The only observable functional change I can think of is with entities that have been deleted in the database after the entity was originally retrieved in the context. Previously the query would not return the entity, although the entity would continue to be present in the context. This change in fact makes the behavior more consistent with the snapshot semantics of the DbContext
, although only for a (very useful) subset of the queries.
Note also that this is a popular feature in LINQ to SQL.
Issue Analytics
- State:
- Created 8 years ago
- Comments:8 (4 by maintainers)
Top GitHub Comments
Closing old issue as this is no longer something we intend to implement.
What would be nice is if there was a DbSet<T>.Find overload that took an instance of T and did the matching on the primary key. It also means you can use it from generic methods where you have an entity but don’t know its properties at compile time.
A use case for this is that the input entity may have been constructed in code and might only have its primary key set.
It also means Find is more strongly typed, as you’re passing in the primary key value(s) via the types properties and not just as a params array of objects.
I appreciate there may be workarounds for some of the use cases I mentioned (perhaps querying Local then FirstOrDefault for object equivalence, or using the entity metadata to get the primary key value(s)?) but abstracting this process into a neat Find seems like a good idea, to me at least. I didn’t see this suggestion here so thought I’d mention it.