Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I identify which Linq functions will not call to sql multiple times?

            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?

like image 416
gavin Avatar asked Feb 18 '23 22:02

gavin


2 Answers

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.

like image 54
Corey Adler Avatar answered Feb 20 '23 12:02

Corey Adler


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().

like image 35
AaronLS Avatar answered Feb 20 '23 10:02

AaronLS