-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsNoTracking() with OrderBy() causes N+1 or Deadlock #12337
Comments
related to #6611 |
Problem here is that AsNoTracking makes the query model more complicated - relinq introduces subquery every time it encounteres a result operator (e.g. AsNoTracking, Include, Distinct etc). AsNoTracking gets eliminated from the query model, but because there is orderby afterwards we don't collapse it back to the simple form. So the QM looks like this: from <>f__AnonymousType0<string, List<string>> a in
from Author a in DbSet<Author>
select new {
[a].Name,
List<string> ToList(
from Book b in DbSet<Book>
where ?= (Nullable<int>)Property([a], "Id") == Property([b], "AuthorId") =?
select [b].Title)
}
order by [a].Name asc
select [a] rather than from Author a in DbSet<Author>
order by [a].Name asc
select new {
[a].Name,
List<string> ToList(
from Book b in DbSet<Book>
where ?= (Nullable<int>)Property([a], "Id") == Property([b], "AuthorId") =?
select [b].Title)
} N+1 optimization only gets applied on the outermost projection, which in this case is [a], so the relevant subquery is not being found and optimized. In this particular case we could optimize the QM, move the orderby to the inner subquery and eliminate the outer, but the translation could be a bit tricky. |
@JakobFerdinand for the best results we recommend to use AsNoTracking / Include operators directly after DbSet, like so: from a in ctx.Authors.AsNoTracking()
select ... |
Separately, we should investigate the async case for deadlock - TaskLiftingExpressionVisitor might not be catching this scenario |
@maumar The Problem with using Something like: IQueryable<Author> GetAuthorsWithBooks()
=> from a in context.Authors
select new
{
a.Name,
Books = a.Books.Select(b => b.Title).ToList()
};
// Service A:
GetAuthorsWithBooks().AsNoTracking().ToList();
// Service B:
GetAuthorsWithBooks().ToList(); Of cause we could use something like: IQueryable<Author> GetAuthorsWithBooks(bool asNoTracking = false)
=> from a in (asNoTracking ? context.Authors.AsNoTracking() : context.Authors)
select new
{
a.Name,
Books = a.Books.Select(b => b.Title).ToList()
}; But we think it´s a bit tideous - specialy after we got used to the great fluent API for all our queries. |
@JakobFerdinand fair enough. If you can re-structure your code so that AsNoTracking is applied as last operation (like what you already did in the second query), that produces queries that are easy to translate also. |
Triage:
|
Works on 3.1 |
When using
AsNoTracking()
beforeOrderBy()
theToList()
causes a N+1 Query andToListAsync()
ends in a deadlock.When adding
AsNoTracking()
after theOrderBy()
statement everything works like expected (2 queries).Maybe relevant to #4007
Further technical details
EF Core version: 2.1.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 1803
IDE: Visual Studio 2017 15.7.3
The text was updated successfully, but these errors were encountered: