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?
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,
costCentersis an IQueryable, because all you do is define a query, but not call ToList yet.lstCostCenteris 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 caselstCostCenter) 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
lstCostCenteris 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 dolstCostCenter.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().