Wednesday, September 28, 2016

Use of First(), FirstOrDefault() and Single(), SingleOrDefault()

In this post let’s see the difference between Enumerable.First() and Enumerable.Single() and of course with there null handling counter parts which are Enumerable.FirstOrDefault() and Enumerable.SingleOrDefault().

Let’s go by a very simple example. Consider the following Employee POCO class and a List<Employee>. For the sake of argument, let’s say Id is a primary key and cannot be duplicated.
public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
List<Employee> employees = new List<Employee>()
{
   new Employee()
   {
       Id = 1,
       FirstName = "Jaliya",
       LastName = "Udagedara"
   },
   new Employee()
   {
       Id = 2,
       FirstName = "John",
       LastName = "Smith"
   }
};
Now imagine, I want to get the Employee who is having a particular Id. In this case, I can use either First() or Single().
employee = employees.First(e => e.Id == 1);
employee = employees.Single(e => e.Id == 1);
Technically there is nothing wrong using either of them, but practically or in the best practice perspective, it’s ideal to use Single() instead of First(). Because when we say First(), it means that out of set of matching rows, I am interested in the First() item. But in our case, we know that there can only be one Employee having a particular IdFirstOrDefault() and SingleOrDefault() are just null handling counter parts of First() and Single(). Basically what it will do is, it will return null if no matching record is found, where as First() and Single() will throw System.InvalidOperationException: Sequence contains no matching element when no matching record is found.

Now consider the following example where I want to get the Employee whose FirstName starts with a particular letter/word (well, it's not a ideal scenario, but again for the sake of argument).
employee = employees.First(e => e.FirstName.StartsWith("J"));
employee = employees.Single(e => e.FirstName.StartsWith("J"));
Here using First()/FirstOrDefault() is the ideal approach as there can be many employees whose FirstName starts with a particular letter/word. But use of Single()/SingleOrDefault() in such scenario will throw System.InvalidOperationException: Sequence contains more than one matching element as there can be many rows which matches that criteria.

One key difference to keep in mind though. In LINQ to SQL, when running against a IQueryableFirst()/FirstOrDefault() will be issuing a SELECT TOP (1) query,  while Single()/SingleOrDefault() issues a SELECT TOP (2). That is because in Single()/SingleOrDefault(), it needs to see whether there are more than one record which matches the given criteria.

So what to get from this? Always use Single()/SingleOrDefault() if you know that there can be only one record which matches the given criteria as that will improve the code readability. Use First()/FirstOrDefault() if you know that there can be many records which matches the criteria and you are only interested in the first record.

Hope this helps.

Happy Coding.

Regards,
Jaliya

No comments:

Post a Comment