LINQ .FirstOrDefault() and .DefaultIfEmpty()

Couple of quick questions

What is the “OrDefault” part of “FirstOrDefault()” ?

Do some tables have a “default” record that acts like a template for new records? If you used FirstOrDefault() in your LINQ expression, on a table(that has a “default” record), but no records that satisfy the Where() part, would that default record be used for the row object?

And if DefaultIfEmpty() is used with FirstOrDefault, and the table does have a “default” record, would the field in the default record be returned, or would the value specified in the DefaultIfEmpty() part be used.

Also, does the DefaultIfEmpty() only apply when no record is found? Or would it apply if a record was found, but the specified field was null? In other words, does the “Empty” part of that refer to the dataset, or the desired field?

1 Like

First() will throw an exception if nothing is found.

FirstOrDefault() returns the default value of the Data Type used if nothing is found, for example in case of integer it would be 0, in case of any Reference type like classes or objects it is NULL.

FirstOrDefault() I think it is the equivalent of DefaultIfEmpty(NULL) or DefaultIfEmpty(0) in above examples. It depends on what the LINQ query is supposed to return.

The Default part of FirstOrDefault means the default of the type being enumerated. For almost all of the uses in Epicor for picking data from tables, this will be null since they are “reference types.” That means they are fully fledged Objects instead of primitives or structs like int, string, decimal, etc. So if you’re calling Db.EmpBasic.FirstOrDefault([criteria]), you might get an EmpBasicRow back, or you might get null back.

DefaultIfEmpty will return the source enumeration if it has elements in it, but if the source enumeration is empty (no elements in it. This is different than the enumeration not existing, which would throw a NullReferenceException), then DefaultIfEmpty returns an enumeration with 1 element in it. This 1 element is what you specify in the call to DefaultIfEmpty.

The difference between the two is sublte: DefaultIfEmpty returns an enumeration (a subset of the source enumeration), while FirstOrDefault returns an actual value from the source enumeration. If FirstOrDefault finds no matches, you will need to check for null or else you’ll get a NullReferenceException if you try to use the return value. If DefaultIfEmpty is used with a Where – Db.EmpBasic.Where([criteria]).DefaultIfEmpty(null) – you will get an enumeration with 1 element which is null.

So with the sample DB table Test, with records:

Key1   PartNum   PartDesc       Index   Code
====   =======   ===========    =====   ====
1      ABC-123   Widget 1       7       99
2      ABC-999   Widget lube    0       <null>

Where field:

  • Level is a non-nullable integer.
  • Code is a nullable integer.
int i;
Nullable<int> c = null;
string s = "ABC-123";
i = Db.Test
  .Where( r =>r.PartNum == s)
  .Select( r =>r.Index).DefaultIfEmpty(11).FirstOrDefault();

c = Db.Test
  .Where( r =>r.PartNum == s)
  .Select( r =>r.Code).DefaultIfEmpty(24).FirstOrDefault();

So…

  • When s = ABC-123, i = 7, and c = 99
  • When s = ABC-999, i = 0, and c = <null> or 24 ??
  • When s = XXX-999, i = 11, and c = <null> or 24 ??

If s = XXX-999 and the .DefaultIfEmpty(11) was removed from the expressions for i,
would it set i = 0 and c = <null> ??

1 Like

When s = ABC-999, c should be null. This is the same null as is in the table.
When s = XXX-999, c should be 24. The Where() call returns an empty list, as does the following Select(). DefaultIfEmpty() sees it’s being called on an empty list and returns a list with 1 element: (24). FirstOrDefault() takes the first element of that list and returns 24.

Yes, calling with s = XXX-999 and DefaultIfEmpty(11) removed would set i = 0 and c = null.

2 Likes