Why does forcing materialization using ToList()
make my query orders of magnitude faster when, if anything, it should do the exact opposite?
1) Calling First()
immediately
// "Context" is an Entity Framework DB-first model
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
var User = query.First();
// ** The above takes 30+ seconds to run **
2) Calling First()
after calling ToList()
:
var query = from x in Context.Users
where x.Username.ToLower().Equals(User.Identity.Name.ToLower())
select x;
var User = query.ToList().First(); // Added ToList() before First()
// ** Now it takes < 1 second to run! **
Update and Resolution
After getting the generated SQL, the only difference is, as expected, the addition of TOP (1)
in the first query. As Andyz Smith says in his answer below, the root cause is that the SQL Server optimizer, in this particular case, chooses a worse execution plan when TOP (1)
is added. Thus the problem has nothing to do with LINQ (which did the right thing by adding TOP (1)
) and everything to do with the idiosyncrasies of SQL Server.