ToList() or not ToList()

Should I use ToList() and if so what is the correct context.

Example 1: is this code ‘good practice’ with ToList()

foreach (var LaborDtl_xRow in (from LaborDtl_Row in Db.LaborDtl //Find all LaborDtl records for this JobNum, AssemblySeq, OprSeq where LaborDtl_Row.Company == ttLaborDtl_xRow.Company && LaborDtl_Row.JobNum == ttLaborDtl_xRow.JobNum && LaborDtl_Row.AssemblySeq == ttLaborDtl_xRow.AssemblySeq && LaborDtl_Row.OprSeq == ttLaborDtl_xRow.OprSeq select LaborDtl_Row).ToList())
      {
        doneqty = doneqty + (LaborDtl_xRow.LaborQty + LaborDtl_xRow.ScrapQty + LaborDtl_xRow.DiscrepQty);
        remqty = remqty - (LaborDtl_xRow.LaborQty + LaborDtl_xRow.ScrapQty + LaborDtl_xRow.DiscrepQty);
      }

Example 2: is this code ‘good practice’ without ToList()

foreach (var LaborDtl_xRow in (from LaborDtl_Row in Db.LaborDtl //Find all LaborDtl records for this JobNum, AssemblySeq, OprSeq where LaborDtl_Row.Company == ttLaborDtl_xRow.Company && LaborDtl_Row.JobNum == ttLaborDtl_xRow.JobNum && LaborDtl_Row.AssemblySeq == ttLaborDtl_xRow.AssemblySeq && LaborDtl_Row.OprSeq == ttLaborDtl_xRow.OprSeq select LaborDtl_Row))
      {
        doneqty = doneqty + (LaborDtl_xRow.LaborQty + LaborDtl_xRow.ScrapQty + LaborDtl_xRow.DiscrepQty);
        remqty = remqty - (LaborDtl_xRow.LaborQty + LaborDtl_xRow.ScrapQty + LaborDtl_xRow.DiscrepQty);
      }

Absolutely, don’t ever iterate over a Db context without first making a list or getting a single result… Breaks things!

4 Likes

Old Whitepaper by @Bart_Elia who has yet to finish it.

https://epiusers.help/uploads/default/original/2X/3/306a5eaf130ae68da6750378540bc9bd0b87d95c.docx

I use it out of habit… But I also have a habit of always doing Db.Table.With(LockHint.NoLock). If you ask the LINQ/EF crowd that actually deal with LINQ in more ways than we will ever in Epicor, that understand Lazy, Eager loading - they would prob advice against it. But when it comes to BPMs I found it easier and faster to work with .ToList() in Memory and not have alot of SQL Calls.

I always use .ToList()
BPMExample.docx (233.4 KB)

5 Likes

As an aside reminder also never directly join a tt table and db table together either. It will first pull the entire DB table 100% then do the join in object memory land.

2 Likes

Yes! You might not notice it on a small dataset, but as your db grows you will take a big perf hit!

1 Like

Another Aside… Always use .Any() and not Count. @Bart_Elia concurs :slight_smile:

2 Likes

I smell an insights presentation. LINQ Query Performance Tips

1 Like

Yes :slight_smile: people need to stop doing SELECT *

If we counted the carbon emission SELECT * causes in a year, there would a revolution to safe the planet. All that wasted compute power. If not for yourself, do it for the planet.

1 Like

I finished the advanced bpm doc. It’s called functions now :rofl:

I actually forgot about this paper. I have a few things to add when back out of the mountains next week.

No pc and one bar of signal for a week makes a relaxed Bart.

4 Likes

Don’t worry we’ve been busy awaiting your return
image

List<Presenter> PresenterList = new List<Presenter>();

PresenterList.Add("Jose Gomez");

Do I use Dependency Injection to get Josh on there? :thinking:

2 Likes

It’s the other way around I implement his abstract classes

1 Like

Loosely coupled is probably a more accurate representation

1 Like

Scope Creep aka Undocumented Feature @Chris_Conn in there.

2 Likes

Haha… Scope? Isnt that just some things you say that will all be irrelevant tommorow?

2 Likes

I have been converting almost 100% of my c# queries to Lamda expressions… once I figured out the protocol, it made more sense to me… A sample of the type of query I find needing alot is below:

//to find if a part exists:
bool partExists = Db.Part.Any(x=>x.Company == CompanyID && x.PartNum ==  myPart);
//to retrieve just the part's description:
string partDesc = Db.Part.Where(x=>x.Company == CompanyID && x.PartNum ==  myPart).Select(x=>x.PartDescription).FirstOrDefault() ?? "Not Found";
//to retrieve multiple field values:
var PRecord = Db.Part.Where(x=>x.Company == CompanyID && x.PartNum ==  myPart).Select(x=> new {x.PartDescription,x.IUOM}).FirstOrDefault();
//remember... we need to check for PRecord is null before processing
2 Likes

Another one I like is:

string partDesc = Db.Part.Where(x=>x.Company == CompanyID && x.PartNum ==  myPart).Select(x=>x.PartDescription).DefaultIfEmpty("Not Found").First();
1 Like

I think I have an internal paper from the early days of E10 - and Entity Framework to be honest. We worked with the EF team at MSFT and their architect at the time and us did the initial Data Layer for E10 and proofed out auto-compilation and a bunch of things that eventually made it into EF.

A couple of things to understand about ToList()

ToList 'resolves the query’
That means it grabs all the data, pulls it to the ‘client’ (e.g. the app server). This can be wanted or a horrible idea.
For example, we use this in the data layer to prevent needless db queries and provides the perf boost you get in E10 over E9. If you are in a transaction, make the same query with the same parameters over and over and over again, just use the last db query. (I am looking at all code in data triggers and data directives that will never abuse the db versus E9). When the transaction completes, the result cache is cleared. That simple behavior made HUGE perf and scale gains in 9 vs 10.

The bad?
If you are looking at a huge list of data that you intend to pull to the app server and then compared to a list in memory - horrible idea. I think someone mentioned the joining of ttRows and DB Rows as an example of that.
I would love to have a batter solution available in a widget but the best approach on those scenario is similar to what was done in the Stored Procedures. Query for the data in the db and cache it in SQL memory or in a db table i you have to. Then join against that data. This performs much quicker.

1 Like