var costCenters = from c in dbContext.CostCenters //no sql call here
orderby c.DisplayRank descending
select c;
List<CostCenter> lstCostCenter = costCenters.ToList();//Immediate execution to sql the first time
lstCostCenter = costCenters.ToList();//no Sql call ??
int test = costCenters.Count();//Call Sql everytime
test = costCenters.Count();//Call Sql again???
I am using Entity Framework 5
I'm starting to learn Linq. I am really confused as to which immediate execution functions will call to SQL everytime. As you can see in the above sample, both ToList() and Count() are immediate execution function, but only Count() will connect to sql on subsequence calls. ToList() connects to sql 1 time, but Count() will connect to Sql everytime.
How do I identify which linq functions will not get call to sql multiple times?
First of all, ToList() never uses deferred execution. It always actualizes the information right away. It doesn't keep going back to database every time because it's already grabbed all of the entities.
To know which operators do what, just take a look at this link.
The difference is the first time you call ToList() it converts the IQueryable(which is just a definition of a query) to a list of IEnumerable(querying the database). In other words you get back a list of objects that are now in memory, so any further LINQ calls on the resulting list use the IEnumerable version that works with in-memory objects. Additionally EF has a feature which caches results, so even if you call ToList on the original IQueryable reference, it is likely to use the in-memory objects instead of fetching them from the database. I am guessing that Count hits the database again instead of counting the cached results, because the query for count is not the same query for the ToList(it is a type of aggregate/grouping), and also perhaps it is designed that way because the DB engine is more efficient at providing a count.
In your example, costCenters
is an IQueryable, because all you do is define a query, but not call ToList yet. lstCostCenter
is the IEnumerable representing the in-memory results from the query after being executed with ToList. Usually immediate calls that produce results, .Count, .ToList, etc. when performed on IQueryable will result in a DB call(the exception being when it finds cached results that it can reuse), and calls on the IEnumerable object(in your case lstCostCenter
) will operate in-memory.
To get more predictable results, then call ToList on the IQueryable first, and make all further calls on the IEnumerable. In other words, anything you call on lstCostCenter
is guaranteed not to hit the database. This is usually the best way to handle it, except if you expect the resulting lists to be large. For example, if lstCostCenter ended up with a 10,000 objects, you probably wouldn't then want to do lstCostCenter.Where(x=>x.Blah > 5)
because that would loop over all 10,000 objects in memory to filter them. In such a case it would be better to modify the query first by appending the additional call to the IQueryable and then calling ToList so that we leverage the DB engine which is better at handling large sets: costCenters.Where(x=>x.Blah > 5).ToList()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With