DEV Community

Cover image for Three common LINQ mistakes and how to fix them
Cesar Aguirre
Cesar Aguirre

Posted on • Updated on • Originally published at canro91.github.io

Three common LINQ mistakes and how to fix them

I originally posted an extended version of this post on my blog.

LINQ is the declarative and lazy-evaluated way of working with collections. It's not that complicated to start working with LINQ to replace for, foreach, and other loops. But, often we make some common mistakes when working with LINQ. Let's learn three common mistakes we make when working with LINQ for the first time and how to fix them.

Mistake 1: Use Count instead of Any

We should always prefer Any over Count to check if a collection has any elements or has at least one element that meets a condition.

Let's write,

movies.Any(); //πŸ˜€
Enter fullscreen mode Exit fullscreen mode

Instead of,

movies.Count() > 0; //πŸ˜•
Enter fullscreen mode Exit fullscreen mode

The Any method returns when it finds at least one element, but the Count method evaluates the entire query. This could be a performance hit for large collections.

Mistake 2: Use Where followed by Any

We can use a condition with Any directly, instead of filtering first with Where to then use Any.

Let's write,

movies.Any(movie => movie.Rating == 5); //πŸ˜€
Enter fullscreen mode Exit fullscreen mode

Instead of,

movies.Where(movie => movie.Rating == 5).Any(); //πŸ˜•
Enter fullscreen mode Exit fullscreen mode

The same applies to the Where method followed by FirstOrDefault, Count, or any other method that receives a filter condition. We could use the filter condition directly instead of relying on the Where method first.

Mistake 3: Use FirstOrDefault without null checking

Let's always check if we have a result when working with FirstOrDefault, LastOrDefault, and SingleOrDefault.

When any of the three above methods don't find results, they return the default value of the collection type.

For objects, the default value would be a null reference. And, do you know what happens when we access a property or method on a null reference?... Yes, It throws the fearsome NullReferenceException. Arrggg!

We have this mistake in the following code sample. We forgot to check if the worst variable has a value.

var movies = new List<Movie>
{
    new Movie("Titanic", 1998, 4.5f),
    new Movie("The Fifth Element", 1995, 4.6f),
    new Movie("Terminator 2", 1999, 4.7f),
    new Movie("Avatar", 2010, 5),
    new Movie("Platoon", 1986, 4),
    new Movie("My Neighbor Totoro", 1988, 5)
};

var worst = movies.FirstOrDefault(movie => movie.Rating < 2);

// We forgot to check for nulls after using FirstOrDefault
// It will break πŸ’£πŸ’£πŸ’£
Console.WriteLine($"{worst.Name}: [{worst.Rating}]");
//                  πŸ‘†πŸ‘†πŸ‘† 
// System.NullReferenceException: 'Object reference not set to an instance of an object.'
//
// worst was null.

Console.ReadKey();

record Movie(string Name, int ReleaseYear, float Rating);
Enter fullscreen mode Exit fullscreen mode

Notice we wrote a LINQ query with FirstOrDefault looking for the first movie with a rating lower than 2. But, we don't have any movie that matches that condition. The FirstOrDefault method returned null and we forgot to check if the worst variable was different from null before using it.

An if (worst != null) would have solved the problem.

There are other alternatives to get rid of the NullReferenceException like nullable reference types from C# 8.0, the DefaultOrEmpty method or FirstOrDefault with an optional default value of our choice from .NET 6.

VoilΓ ! Those are the three most common LINQ mistakes. I know they seem silly, but we often overlook them. If you want to take a deeper look at LINQ and all of its features, check my Quick Guide to LINQ.

Hey! I'm Cesar, a software engineer and lifelong learner. Visit my blog to learn more about my work! If you want to support my work, check my Getting Started with LINQ course on Educative.

Happy coding!

Top comments (2)

Collapse
 
stphnwlsh profile image
Stephen Walsh

Ran a quick benchmark because I was interested in how different the execution times are......wow!!!! Any is far and away the way to go!!!

|     Method |     N |          Mean |        Error |       StdDev |        Median | Rank |  Gen 0 | Allocated |
|----------- |------ |--------------:|-------------:|-------------:|--------------:|-----:|-------:|----------:|
|        Any |   100 |      20.45 ns |     0.439 ns |     0.411 ns |      20.41 ns |    1 | 0.0051 |      32 B |
|        Any | 10000 |      20.74 ns |     0.387 ns |     0.625 ns |      20.55 ns |    1 | 0.0051 |      32 B |
|   WhereAny | 10000 |      48.41 ns |     2.717 ns |     7.795 ns |      46.36 ns |    2 | 0.0076 |      48 B |
|   WhereAny |   100 |      48.90 ns |     0.997 ns |     1.148 ns |      48.53 ns |    3 | 0.0076 |      48 B |
| WhereCount |   100 |     266.11 ns |     4.956 ns |     7.265 ns |     263.63 ns |    4 | 0.0076 |      48 B |
|      Count |   100 |     731.84 ns |    10.548 ns |    11.724 ns |     729.25 ns |    5 | 0.0048 |      32 B |
| WhereCount | 10000 |  51,727.62 ns | 1,246.956 ns | 3,617.646 ns |  49,829.62 ns |    6 |      - |      48 B |
|      Count | 10000 | 107,674.92 ns | 2,148.017 ns | 2,205.855 ns | 109,243.71 ns |    7 |      - |      32 B |
Enter fullscreen mode Exit fullscreen mode
Collapse
 
canro91 profile image
Cesar Aguirre

Thanks Stephen for your comment. Yeah, Any returns as soon as it finds at least one element...Wow, I was planning another post in this series to show precisely that benchmark...