Best practice to check for null in LINQ query - c#

Assuming I have a LINQ query as such ...
var selectedUser = myDb.Users.Where<User>(u => u.Email == email).Single<User>();
What is the best way to see if I received a result back? Do I just check if selectedUser != null
if (selectedUser != null)
{
// OK, not null so go ahead and do stuff
}

Single() will throw an exception if there are no matches.
SingleOrDefault() will return a single element, or a default value. However it will throw exception if there are more than one element in the sequence.
If you are ok with just taking the first element if there happens to be more than use FirstOrDefault() for this:
var selectedUser = myDb.Users.FirstOrDefault(u => u.Email == email);
Then just check if it is null like normal:
if (selectedUser != null)
{
// OK, not null so go ahead and do stuff
}
Side Note In either case you shouldn't need your explicit <T> definitions. Also the Where() clause is redundant, you can pass your lambda in FirstOrDefault(), Single(), SingleOrDefault(), etc.

.Single will throw an exception if there isn't a matching item. Instead use SingleOrDefault and check for null.

Related

Handling linq XML queries that do not return an element (and thus throw an exception)

I am writing a linq query to return an IEnumerable of XElements that match the search criteria.
The program throws an exception when an element matching the search criteria isn't found and including the statement inside a try/catch block doesn't catch the exception.
Thoughts on the correct way to catch the exception?
try
{
IEnumerable<XElement> oFootnotes = oRoot.DescendantNodes().OfType<XElement>().Where(x => x.Name == "p" && x.Attribute("class").Value == "endnote" && x.Attribute("id").Value == idFootnote);
}
catch(Exception ex)
{
}
Enumerable.Where() will not throw an exception when an element matching the search criteria isn't found. It will simply return an empty enumerable.
You have two possible problems:
You have some elements that are missing the attributes "id" or "class".
To resolve this, use null conditional operator ?. to access their value:
.Where(x => x.Name == "p" && x.Attribute("class")?.Value == "endnote" && x.Attribute("id")?.Value == idFootnote);
Somewhere outside the code shown, you are getting the first element of the enumerable by using Enumerable.First().
To fix this, use Enumerable.FirstOrDefault() instead, and check for a null return.
Thus your fixed query might look like:
var oFootnotes = oRoot.Descendants().Where(x => x.Name == "p" && x.Attribute("class")?.Value == "endnote" && x.Attribute("id")?.Value == idFootnote);
string firstFootnoteText = oFootnotes.FirstOrDefault()?.Value ?? ""; // If you want you can return the empty string in preference to the null string using the null-coalescing operator ??
Using Descendants() to find all descendant elements of an XElement is equivalent to, but more concise than, DescendantNodes().OfType<XElement>().
Demo fiddle here.

Cosmos DB read call

I'm making a call to Azure Cosmos DB where I know the data I'm querying does NOT exist.
I was expecting to get a null value but instead I'm getting:
Enumeration yielded no results
How do I test whether I received a value or not? I was testing for null which doesn't work because the outcome is not null.
My code looks something like this:
var result = await _client.ReadQuery<myObject>(AccountsCollection, sql, pa);
if(result == null)
return null;
Instead of just checking on result == null you should use the LINQ extension method .Any() to see if there are any items that match a condition (in your case the condition is just anything existing in the collection):
if(result == null || !result.Any())
{
return null;
}

Filtering results by user name

I have this code:
// GET: Teachers/Edit/5
public ActionResult Edit(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
string uname = User.Identity.GetUserId();
Teachers teachers = db.teachers.Find(id);
if (teachers == null)
{
return HttpNotFound();
}
ViewBag.GroupID = new SelectList(db.groups, "GroupID", "GroupName", teachers.GroupID);
return View(teachers);
}
I am attempting to filter my returned results by id (which is my DB key) but also by the user name which is also stored into the database but not as a key.
I'm not sure of the syntax, but I am pretty sure find is not the way to go.
You can use SingleOrDefault:
db.teachers.SingleOrDefault(x => x.ID == id && x.UserName == username);
It returns a single, specific element of a sequence, or a default value if that element is not found. It will throw exception if there is more than one element in the result.
Additionally, you can also use Single(), First(), FirstOrDefault() in such places. Here is the differences between them:
First() - throws exception if empty or not found, does not throw if duplicate
FirstOrDefault() - returns default if empty or not found, does not throw if duplicate
Single() - throws exception if empty or not found, throws if duplicate exists
SingleOrDefault() - returns default if empty or not found, throws if duplicate exists
P.S: You haven't stated your column names. So, I have just guessed their names. You can change them as you wish.

Sequence contains more than one element - SingleOrDefault not helping

I have the line below but still get an exception "Sequence contains more than one element"
Details rd = this.db.Details.SingleOrDefault(x => x.TId == Id && x.TypeId == TypeId);
I was hoping that SingleOrDefault would avoid the exception.
SingleOrDefault returns a SINGLE element or null if no element is found. If 2 elements are found in your Enumerable then it throws the exception you are seeing. Just like Highlander... with Single - there can be only one.
FirstOrDefault returns the FIRST element it finds or null if no element is found. so if there are 2 elements that match your predicate the second one is ignored.
Assuming you don't care if there are multiple matches and you only want the first one or null if no match is found... then you probably want the following...
Details rd = this.db.Details
.FirstOrDefault(x => x.TId == Id && x.TypeId == TypeId);
Note that both of these methods only return one element they only differ in what they do after they find a match. First stops looking at that point and returns what it found, Single keeps checking the rest of the list to make sure there are no more matches. The OrDefault part determines what it returns if no match is found. SingleOrDefault or FirstOrDefault returns null if no value is found but if you just use Single or First then it MUST find one match or it will throw an exception.
EDIT: Good point Steve
Since First returns the first element you may need to use an OrderBy in order to make sure the element you want is indeed first. For instance... suppose your object had an UpdateDate property and you wanted the object with the most recent UpdateDate...
Details rd = this.db.Details
.OrderByDescending(x => x.UpdateDate)
.FirstOrDefault(x => x.TId == Id && x.TypeId == TypeId);
In case you are having a list, convert the list to IEnumerable list, then you can make use of FirstOrDefault method
IEnumerable<BuyOnlineSearchdetails> details = new List<BuyOnlineSearchdetails>();
var FirstRow = details.FirstOrDefault();
string Count = "0";
if (FirstRow != null)
{
Count = FirstRow.TotalCount.ToString();
}
else
{
Count = "0";
}
You have to use FirstOrDefault() instead of SingleOrDefault().
SingleOrDefault throws an exception if more than one element exists where as FirstOrDefault not.

linq where clause and count result in null exception

The code below works unless p.School.SchoolName turns out to be null, in which case it results in a NullReferenceException.
if (ExistingUsers.Where(p => p.StudentID == item.StaffID &&
p.School.SchoolName == item.SchoolID).Count() > 0)
{
// Do stuff.
}
ExistingUsers is a list of users:
public List<User> ExistingUsers;
Here is the relevant portion of the stacktrace:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Enumerable.WhereListIterator1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable1 source)
How should I handle this where clause?
Thanks very much in advance.
I suspect p.School is null, not SchoolName. Simply add a null check before accessing SchoolName. Also, use Any() to check if there are any results instead of Count() > 0 unless you're really in need of the count. This performs better since not all items are iterated if any exist.
var result = ExistingUsers.Where(p => p.StudentID == item.StaffID
&& p.School != null
&& p.School.SchoolName == item.SchoolID)
.Any();
if (result) { /* do something */ }
For all database nullable columns, we should either add null check or do simple comparision a == b instead of a.ToLower() == b.ToLower() or similar string operations.
My observation as below:
As they get iterated through Enumerable of LINQ Query for comparision against with input string/value, any null value (of database column) and operations on it would raise exception, but Enumerable becomes NULL, though query is not null.
In the case where you want to get the null value (all the student, with school or not) Use left join.
There are a good example on MSDN
If I remember correctly (not at my developer PC at the moment and can't check with Reflector), using the == operator results in calling the instance implementation string.Equals(string), not the static implementation String.Equals(string, string).
Assuming that your problem is due to SchoolName being null, as you suggest, try this:
if (ExistingUsers.Where(
p => p.StudentID == item.StaffID
&& String.Equals( p.School.SchoolName, item.SchoolID)).Count() > 0)
{
// Do stuff.
}
Of course, comments by other answers count as well:
Using Any() instead of Count() > 0 will generally perform better
If p.School is the null, you'll need an extra check
Hope this helps.

Categories

Resources