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