Difference between foreach and just one record

I’m not a native C# programmer, however I do program in other languages such as PHP which is interpreted rather than compilied. Anyways…

In a lot of C# code we have had written for us I see code like this:

 // Get value from Customer Part Cross Reference
        foreach(var custxprt in (from ttrow in Db.CustXPrt.With(LockHint.NoLock)
                                 where ttrow.Company == od.Company
                                    && ttrow.CustNum == od.CustNum
                                    && ttrow.PartNum == od.PartNum
                                    && ttrow.OtherField == od.OtherField
                                  select new {ttrow.MyCustomField_c}) ) {
         string myCustomValue = custxprt.MyCustomField_c;

        }

The thing is we’re only expecting this CustXPrt record to have exactly one match. So I asked AI another way to get this record and it came up with this:

// Get value from Customer Part Cross Reference
var custxprt = (from ttrow in Db.CustXPrt.With(LockHint.NoLock)
                where ttrow.Company == od.Company
                   && ttrow.CustNum == od.CustNum
                   && ttrow.PartNum == od.PartNum
                   && ttrow.OtherField == od.OtherField
                select new { ttrow.MyCustomField_c }).FirstOrDefault();

if (custxprt != null) {
    string myCustomValue = custxprt.MyCustomField_c;
}

It seems a whole lot cleaner and straightforward. There is no foreach which I think in this instance. We don’t have to worry about closing the ending bracket (and indentation or lack thereof – I could go on for minutes about this topic) or other variables being in or out of scope because of the foreach structure, etc.

I’m curious to know, was the person who made the foreach just lacking in foresight to know how many records were to come back? I think we’ve got a copy-paste job cuz I see foreaches everywhere. It just feels like a lack of finesse / skill or copy-pasting laziness on the original programming.

Why are Epicor BPMs and their examples littered with foreaches when only one record is required? Why create all that extra code and programming overhead? I’m not sure maybe the compiler makes it more efficient in the background, I’m not too experienced with compiled languages.

1 Like

The second way using FirstOrDefault() when you’re only expecting one row is better. In your example, you’re querying the DB, so that will work just fine.

The thing is, for the temp tables / dataset on an update, you have to know which row you’re looking for. In a BPM, for example, you might have a temp table with the unchanged row and the updated row that you’re changing, so you need to be sure to specify where tt.RowMod == "U" or tt.Updated() to make sure you’re looking at the correct row in temp tables.

I usually go a step further than this, and use the lambda expression to make it look even cleaner:

var custxprt = Db.CustXPrt.Where( x.Company == od.Company
                               && x.CustNum == od.CustNum
                               && x.PartNum == od.PartNum
                               && x.OtherField == od.OtherField
                          .Select( s => s.MyCustomField_c )
                          .FirstOrDefault();

if ( custxprt == null ) return;

string myCustomValue = custxprt; // One field & row selected, so original var == MyCustomField_c value
3 Likes

I won’t pretend to know the answer, but I assumed it was to make sure if there are more than one match, that all matches get the same treatment. If you only expect one match, then after the first match any other matches can simply return an error or ignore if blank. If you always grab first or default, then you will never know if more than one match was made.
:person_shrugging: