DEV Community

Cover image for Three Common LINQ Mistakes and How To Fix Them
Cesar Aguirre
Cesar Aguirre

Posted on • Edited 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.


It's easy to start working with LINQ to replace for, foreach, and other loops.

But, often we make some common mistakes when working with LINQ. Here are 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 those three 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. Ooops!

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.

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 when using FirstOrDefault:

Voilà! Those are the three most common LINQ mistakes. I know they seem silly, but we often overlook them.


Want to write more expressive code for collections? Join my Udemy course, Getting Started with LINQ, and master everything you need to work productively with LINQ — all in less than two hours!

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...