Visual Studio snippets for Moq

These days, I use Moq a lot. There are things I like and I don’t like about creating fakes with Moq. But it’s simple and easy to use.

I use the same four Moq methods all the time. Setup, ReturnAsync, ThrowsAsync and Verify. That’s all you need. I decided to create snippets inside Visual Studio to avoid typing the same method names every time. These are the snippets I use.

Create a Mock with Moq

Use mn to create a Mock. It expands to var mock = new Mock<>();.

mn to create a Mock
Use mn to create a Mock

Setup and Return

With a mock instance, use mr to Setup a method to Return something.

mr to Setup/Return
Use mr to Setup/Return

Setup and ThrowsException

If you want to throw an exception from your mock, use mt.

mt to Setup/ThrowsException
Use mt to Setup/ThrowsException

Also, you can use mra and mta for the asynchronous version of Return and ThrowsException respectively.

If you want to use the same snippets I use, download the snippets file from VSMoqSnippets repository.

To load snippets into Visual Studio, from the “Tools” menu, choose “Code Snippets Manager” and import the snippets file.

canro91/VSMoqSnippets - GitHub

Voilà! Those are the snippets I use for Moq. Check my Visual Studio setup for more settings and extensions. Do you want to learn more about fakes? Read what are fakes in unit testing and these tips for better stubs and mocks.

Happy coding!

Decorator pattern. A real example in C#

I’ve been working with Stripe to take payments. Depending on the volume of requests you make to the Stripe API, you might exceed the maximum number of requests per second. This is how we can implement a retry mechanism using the Decorator pattern in C#.

A Decorator wraps another object to extend its responsabilities, without modifying its existing behavior, while keeping the same signature of public methods. Decorators are used to add orthogonal responsibilities like logging, caching and retrying.

Let’s use Marvel movies to understand the Decorator pattern. When IronMan wore the HULKBUSTER suit in the Age of Ultron, he implemented the Decorator pattern. He had a new functionality, stopping the Hulk, while keeping his same functions, being IronMan. I hope you got it!

Decorator pattern. A real example in C#
When you wear a jacket, you use the Decorator pattern too. Photo by Archie on Unsplash

Naive Retry logic

Let’s start with a PaymentService. This service collects everything it needs to start a payment with Stripe. For example, customer id, fees, destination account, etc. Then, it uses a PaymentIntentService to call Stripe API using its C# client.

This would be the CreatePaymentIntentAsync() method inside the PaymentService.

public class PaymentService : IPaymentService
{
    private readonly IPaymentIntentService _paymentIntentService;
    private readonly IFeeService _feeService;

    public PaymentService(IPaymentIntentService paymentIntentService, IFeeService feeService)
    {
        _paymentIntentService = paymentIntentService;
        _feeService_ = feeService;
    }

    public async Task<PaymentIntentDetails> CreatePaymentIntentAsync(
        PaymentRequestViewModel request,
        IDictionary<string, string> metadata)
    {
        var currencyCode = request.CurrencyCode;
        var description = request.Description;
        var amountInUnits = request.Amount.ToMainUnits();
        var gatewayAccountId = request.GatewayAccountId;

        var applicationFee = _feeService.GetApplicationFee(request);
        metadata.AddFees(applicationFee);

        var paymentIntentOptions = new PaymentIntentCreateOptions
        {
            Amount = amountInUnits,
            Currency = currencyCode,
            ApplicationFeeAmount = applicationFee,
            Description = description,
            Metadata = metadata,
            Confirm = true,
            CaptureMethod = "manual",
            OnBehalfOf = gatewayAccountId,
            TransferData = new PaymentIntentTransferDataOptions
            {
                Destination = gatewayAccountId
            }
        };

        try
        {
            var paymentIntent = await _paymentIntentService.CreateAsync(paymentIntentOptions, GetRequestOptions());

            return GetSuccessfulPaymentIntentDetails(request, paymentIntent);
        }
        catch (StripeException stripeException)
        {
            return GetFailedPaymentIntentDetails(request, stripeException);
        }
    }
}

We want to retry the method CreateAsync() if it reaches the maximum number of allowed requests by Stripe at a given time.

We can add retry logic using a helper method. And, wrap the call to the CreateAsync() method inside the helper method. Something like this,

try
{
    var paymentIntent = await RetryAsync(async () =>
    {
        return await _paymentIntentService.CreateAsync(paymentIntentOptions, GetRequestOptions());
    });

    return GetSuccessfulPaymentIntentDetails(paymentRequest, paymentIntent);
}
catch (StripeException stripeException)
{
    return GetFailedPaymentIntentDetails(paymentRequest, stripeException);
}

The RetryAsync() helper method will execute the API call a fixed number of times if it fails with a TooManyRequests status code. If it fails with a different exception or status code, it propagates the exception to the caller.

This is a simple implementation of a retry method.

protected async Task<TResult> RetryAsync<TResult>(Func<Task<TResult>> apiCommand, int maxRetryCount = 3)
{
    var exceptions = new List<Exception>();
    var retryCount = 0;

    while (true)
    {
        try
        {
            return await apiCommand();
        }
        catch (StripeException ex) when (ex.HttpStatusCode == HttpStatusCode.TooManyRequests)
        {
            exceptions.Add(ex);

            if (retryCount == retryCountMax)
            {
                throw new AggregateException("Too many requests", exceptions);
            }

            retryCount++;
        }
    }
}

Later, we can replace this helper method with a more robust implementation using Polly, for example. It can include incremental delays between failed attempts and timeouts.

But, using this helper method implies wrapping the methods to retry inside our helper method all over our codebase. Hopefully, if we have a singe place to take payments, that wouldn’t be a problem. Also, our PaymentService mixes business logic with retry logic. That’s smelly. We should keep responsabilities separated.

Retry logic with Decorator pattern: Let’s Decorate

For a more clean solution, let’s use the Decorator pattern.

First, let’s create a decorator called RetryablePaymentIntentService for the PaymentIntentService. Since we want to keep the same API of public methods, the decorator should inherit from the same interface, IPaymentIntentService.

public class RetryablePaymentIntentService : IPaymentIntentService
{
    public Task<PaymentIntent> CreateAsync(PaymentIntentCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default)
    {
        // We will fill the details in the next steps
    }
}

The decorator will only handle the retry logic. It will use the existing PaymentIntentService to call Stripe. The decorator will receive another IPaymentIntentService in its constructor.

public class RetryablePaymentIntentService : IPaymentIntentService
{
    private readonly IPaymentIntentService _decorated;

    public RetryablePaymentIntentService(IPaymentIntentService decorated)
    {
        _decorated = decorated;
    }

    public Task<PaymentIntent> CreateAsync(PaymentIntentCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default)
    {
        // We will fill the details in the next steps
    }
}

Notice, we named the field in the decorator, _decorated. And, yes, the decorator inherits and receives the same type. That’s the trick!

Next, we need to fill in the details. To complete our decorator, let’s use our previous RetryAsync() method. Our decorator will look like this,

public class RetryablePaymentIntentService : PaymentIntentService
{
    private readonly PaymentIntentService _decorated;

    public RetryablePaymentIntentService(PaymentIntentService decorated)
    {
        _decorated = decorated;
    }

    public Task<PaymentIntent> CreateAsync(PaymentIntentCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default)
    {
        return RetryAsync(async () =>
        {
            return await _decorated.CreateAsync(paymentIntentOptions, requestOptions, cancellationToken);
        });
    }
    
    // Same RetryAsync method as before...
}

Now, our decorator is ready to use it. In the PaymentService, we can replace the simple PaymentIntentService by our new RetryablePaymentIntentService. Both services implement the same interface.

We can create our decorator like this,

new RetryablePaymentIntentService(new PaymentIntentService(/* Other dependencies */));

Inject Decorators into ASP.NET Core container

Let’s register our decorator

But, if you’re using an ASP.NET Core API project, we can use the dependency container to build the decorator.

Let’s use an extension method AddPaymentServices() to group the registration of our services. You can register your services directly into the Startup class. No problem!

public static class ServiceCollectionExtensions
{
    public static void AddPaymentServices(this IServiceCollection services)
    {
        services.AddTransient<PaymentIntentService>();
        services.AddTransient<IPaymentIntentService>((provider) =>
        {
          var decorated = provider.GetRequiredService<PaymentIntentService>();
          return new RetryablePaymentIntentService(decorated);
        });
        services.AddTransient<IPaymentService, PaymentService>();
    }
}

This time, we registered the original PaymentIntentService without specifying an interface. We only used the IPaymentIntentService to register the decorator. When resolved, the PaymentService will receive the decorator instead of the original service without retry logic.

Let’s use Scrutor to register our decorator

Optionally, we can use Scrutor to register the decorated version of the IPaymentIntentService. Scrutor is a library that adds more features to the built-in dependencies container. Don’t forget to install the Scrutor NuGet package into your project, if you choose this route.

In that case, our AddPaymentServices() will look like this,

public static class ServiceCollectionExtensions
{
    public static void AddPaymentServices(this IServiceCollection services)
    {
        services.AddTransient<IPaymentIntentService, PaymentIntentService>();
        // With Scrutor, we need the method Decorate
        services.Decorate<IPaymentIntentService, RetryablePaymentIntentService>();
        services.AddTransient<IPaymentService, PaymentService>();
    }
}

Notice, this time we have explicitly register two entries for the IPaymentIntentService. The Decorate() method does the trick for us.

Voilà! That’s how we can implement the Decorator pattern to retry API calls. We can also use the decorator pattern to bring logging or caching to our services. Check how you can use the Decorator pattern to add a Redis caching layer with ASP.NET Core.

For more real-world examples, check my post on Primitive Obsession. That’s about handling Stripe currency units. If you want my take on another pattern, check my post about the Pipeline pattern.

Happy coding!

How to write good unit tests: Write failing tests first

A passing test isn’t always the only thing to look for. It’s important to see our test failing. I learned this lesson the hard way. Let’s see why we should start writing failing tests.

To write reliable unit tests, always start writing a failing test. And make sure it fails for the right reasons. Follow the Red, Green, Refactor principle of Test-Driven Development (TDD). Write a failing test, make it pass, and refactor the code. Don’t skip the failing test part.

The Passing test

Let’s continue with the same example from our previous post on how to write good unit tests by reducing noise and using obvious test values.

From our last example, we had a controller to create, update, and suspend user accounts. Inside its constructor, this controller validated some email addresses from an injected configuration object.

After we refactored our test from the last post, we ended up with this:

[TestMethod]
public void AccountController_SenderEmailIsNull_ThrowsException()
{
    var emailConfig = Options.Create(new EmailConfiguration
    {
        SenderEmail = null,
        ReplyToEmail = "email@email.com",
        SupportEmail = "email@email.com"
    });

    Assert.ThrowsException<ArgumentNullException>(() =>
        MakeAccountController(emailConfig));
        // ^^^^^
}

private AccountController MakeAccountController(
    IOptions<EmailConfiguration> emailConfiguration)
    // ^^^^^
{
    var mapper = new Mock<IMapper>();
    var logger = new Mock<ILogger<AccountController>>();
    var accountService = new Mock<IAccountService>();
    var accountPersonService = new Mock<IAccountPersonService>();
    var emailService = new Mock<IEmailService>();
    var emailConfig = new Mock<IOptions<EmailConfiguration>>();
    var httpContextAccessor = new Mock<IHttpContextAccessor>();

    return new AccountController(
            mapper.Object,
            logger.Object,
            accountService.Object,
            accountPersonService.Object,
            emailService.Object,
            emailConfiguration,
            // ^^^^^
            httpContextAccessor.Object);
}
Always start writing a failing test
Always start writing a failing test. Photo by Neora Aylon on Unsplash

A false positive

This time, I had a new requirement. I needed to add a new method to our AccountController. This new method read another configuration object injected into the controller.

To follow the convention of validating required parameters inside constructors, I also checked for this new configuration object. I wrote a new test and a new MakeAccountController() builder method to call the constructor with only the parameters I needed.

[TestMethod]
public void AccountController_NoNewConfig_ThrowsException()
{
    var options = Options.Create<SomeNewConfig>(null);

    Assert.ThrowsException<ArgumentNullException>(() =>
        MakeAccountController(options));
}

// A new builder method
private AccountController MakeAccountController(IOptions<SomeNewConfig> someNewConfig)
{
    var emailConfig = new Mock<IOptions<EmailConfiguration>());
    return CreateAccountController(emailConfig.Object, someNewConfig);
}

private AccountController MakeAccountController(
    IOptions<EmailConfiguration> emailConfig,
    IOptions<SomeNewConfig> someNewConfig)
{
    // It calls the constructor with mocks, except for emailConfig and someNewConfig
}

And the constructor looked like this:

public class AccountController : Controller
{
  public AccountController(
      IMapper mapper,
      ILogger<AccountController> logger,
      IAccountService accountService,
      IAccountPersonService accountPersonService,
      IEmailService emailService,
      IOptions<EmailConfiguration> emailConfig,
      IHttpContextAccessor httpContextAccessor,
      IOptions<SomeNewConfig> someNewConfig)
  {
      var emailConfiguration = emailConfig?.Value
            ?? throw new ArgumentNullException($"EmailConfiguration");
      if (string.IsNullOrEmpty(emailConfiguration.SenderEmail))
      {
          throw new ArgumentNullException($"SenderEmail");
      }

      var someNewConfiguration = someNewConfig?.Value
            ?? throw new ArgumentNullException($"SomeNewConfig");
      if (string.IsNullOrEmpty(someNewConfiguration.SomeKey)
      {
          throw new ArgumentNullException($"SomeKey");
      }

      // etc...
  }
}

I ran the test and it passed. Move on! But…Wait! There’s something wrong with that test! Did you spot the issue?

Make your tests fail for the right reasons

Of course, that test is passing. The code throws an ArgumentNullException. But, that exception is coming from the wrong place. It comes from the validation for the email configuration, not from our new validation.

I forgot to use a valid email configuration in the new MakeAccountController() builder method. I used a mock reference without setting up any values. I only realized that after getting my code reviewed. Point for the code review!

private AccountController MakeAccountController(IOptions<SomeNewConfig> someNewConfig)
{
    var emailConfig = new Mock<IOptions<EmailConfiguration>());
    //  ^^^^^
    // Here we need to setup a valid EmailConfiguration
    return CreateAccountController(emailConfig.Object, someNewConfig);
}

Let’s make sure to start always by writing a failing test. And, this test should fail for the right reasons.

If we write our tests after writing our production code, let’s comment some parts of our production code to see if our tests fail or change the assertions on purpose.

When we make a failed test pass, we’re testing the test. We’re making sure it fails and passes when it should. We know we aren’t writing buggy tests or introducing false positives into our test suite.

A better test for our example would check the exception message. Like this:

[TestMethod]
public void AccountController_NoSomeNewConfig_ThrowsException()
{
    var options = Options.Create<SomeNewConfig>(null);

    var ex = Assert.ThrowsException<ArgumentNullException>(() => 
        MakeAccountController(options));
    StringAssert.Contains(ex.Message, nameof(SomeNewConfig));
}

Voilà! This task reminded me to see my tests fail for the right reasons. Do you have passing tests? Do they pass and fail when they should? I hope they do after reading this post.

For more tips on how to write good unit tests, check my follow-up on using simple test values. Don’t miss the rest of my Unit Testing 101 series where I also cover mocking, assertions, and best practices.

Happy unit testing!

TIL: Livable Code. Living with your mess

These days, I watched a conference online by Sarah Mei (@sarahmei) titled: Livable Code. This is what livable code is.

Livable Code is the analogy that building software is like living an space inside a building instead of constructing a house or a building.

Building software isn’t like building anything else. It isn’t like planning, constructing and maintaining a tower of apartments. It’s living an space where each decision counts towards having a mess or a enjoyable place to live.

Code hoarders

We don’t end up with a messy unlivable code because of a bad choice of frameworks or technologies. Of course, the shape of the space will influence the design of our house. But, messy code comes after small decisions made every day. Patch after patch. One day you receive a package and throw the box and the plastic bags in a corner. The next thing you know is that pile almost reaches the ceiling.

A nice pile of junk
Messy code comes after small decisions made every day. Photo by Lance Grandahl on Unsplash

Everyday Decisions and Perfect Clean Code

We have to live with every decision we have made inside our code. Instead of, building something and moving on, we live with software. We don’t have “done” projects anymore. We don’t ship software into burned CD’s and hand the maintenance to somebody else. We have to get to live with our code and make it livable for the ones around us.

There is no perfect clean code. In fact, it’s unattainable. Perfect clean code only exists on books and blog posts. The same as perfectly design minimalist spaces in magazines. We need certain amount of stuff to live. A pile of books, a video game console. An amount of stuff we can tolerate. We could have spaghetti code that nobody understands or so clean and perfectly crafted code that nobody understands either. Both extremes are unlivable.

4 rules for more livable code

The author in the conference gives 4 rules to make your code more livable.

  1. Don’t make it worse. No matter what you have to do, don’t make it worse. Remind this to other team members when needed.
  2. Improvement over consistency. Ship your technical debt away, one change at a time.
  3. Inline everyday. Don’t have stories for big refactors. Make refactoring an everyday job. Always leave the campground cleaner than you found it.
  4. Liase. Communicate, don’t lie. Treat refactoring as everything you do.
    • Don’t ask for permission. But, be upfront.
    • Don’t ask for forgiveness. But, learn every time.
    • Ask for advice. But, don’t always take it.
    • Work together. We all have to live here!

The most important part of software is not the code, or the people. It’s the system.

Happy code living!

A quick guide to LINQ with examples

Today a friend asked me about LINQ. She was studying for a technical interview. So, dear Alice: This is what LINQ is and these are the most common LINQ methods with examples. All you need to know about LINQ in 15 minutes or less.

Language-Integrated Query (LINQ) is the declarative way of working with collections in C#. LINQ works with databases and XML files too. Apart from extensions methods on the IEnumerable type, LINQ has a query syntax, a SQL-like syntax

1. LINQ is declarative

It means we write our code stating the results we want instead of doing every step to get those results.

With LINQ, we write code to “filter a collection based on a condition.” Instead of writing code to “grab an element, check if it satisfies a condition, then move to the next element, check again…“, etc.

LINQ is a better alternative to query collections using for, foreach, or any other loop. With LINQ, we write more expressive and compact code.

Waiting at a cinema before a movie starts
Photo by Erik Witsoe on Unsplash

2. Let’s find our favorite movies

Let’s start with the collection of movies we have watched. We have a Movie class with a name, release year, and rating. Let’s find our favorite movies, the ones with a rating greater than 4.5.

Here’s a Console application that prints our favorite movies,

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

var favorites = new List<Movie>();
foreach (var movie in movies)
// ^^^^
{
    if (movie.Rating > 4.5)
    //  ^^^^^
    {
        favorites.Add(movie);
    }
}

Console.WriteLine("My favorites:");
PrintMovies(favorites);
// Output:
// My favorites:
// The Fifth Element: [4.6]
// Terminator 2: [4.7]
// Avatar: [5]
// My Neighbor Totoro: [5]

static void PrintMovies(IEnumerable<Movie> movies)
{
    foreach (var movie in movies)
    {
        Console.WriteLine($"{movie.Name}: [{movie.Rating}]");
    }
}

record Movie(string Name, int ReleaseYear, float Rating);

We wrote a foreach loop and an if statement to find movies with a rating greater than 4.5. No LINQ so far!

Also, we used Top-level statements, records and Global usings from recent C# versions. That’s why we didn’t write the Main class and import the System.Linq namespace.

3. Let’s use our first LINQ method: Where

To work with LINQ, we need to be comfortable with delegates and lambda functions.

In a few words: a delegate is a pointer to a method. And a lambda function is a method with only the parameters and the body. C# has two built-in delegates: Func and Action.

How to filter a collection with Where

If we want to filter our list of movies to keep only those with a rating greater than 4.5, the LINQ method to filter collections is Where().

Where returns a new collection with only the elements that meet a condition.

Let’s replace our foreach loop with the Where() method. And let’s use the condition inside the if statement as the filter condition for Where(). Like this,

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

var favorites = movies.Where(movie => movie.Rating > 4.5);
//                     ^^^^^

Console.WriteLine("My favorites:");
PrintMovies(favorites);
// Output:
// My favorites:
// The Fifth Element: [4.6]
// Terminator 2: [4.7]
// Avatar: [5]
// My Neighbor Totoro: [5]

static void PrintMovies(IEnumerable<Movie> movies)
{
    foreach (var movie in movies)
    {
        Console.WriteLine($"{movie.Name}: [{movie.Rating}]");
    }
}

record Movie(string Name, int ReleaseYear, float Rating);

We replaced the foreach and if statements with a single line of code:

var favorites = movies.Where(movie => movie.Rating > 4.5);

More compact, isn’t it? Also, we turned the condition inside the if statement into a lambda function. This is why we need to be comfortable working with delegates.

Where() returned a new collection. It didn’t remove any elements from the original movies list.

LINQ methods don’t change the original collection. They return a result without modifying the original one.

Where accepts a method too

Instead of lambda functions, we can use a method as the filtering condition.

To replace the lambda function from our previous example, let’s create an IsFavorite() method that receives Movie as a parameter and returns bool. For example,

private bool IsFavorite(Movie movie)
  => movie.Rating > 4.5;

Then, we can use IsFavorite() inside the Where() method to filter our movies. Like this,

var favorites = movies.Where(movie => IsFavorite(movie));

Since IsFavorite() only has one parameter, we can remove the parameter name, like this,

var favorites = movies.Where(IsFavorite);

Way more compact and readable than our original version with a foreach and if.

4. Most common LINQ methods

So far, we’ve seen only one LINQ method: Where(). Of course, LINQ has more methods.

Here are the most common LINQ methods:

1. Select

Select transforms a collection by applying a mapping function to every element.

Let’s find only the names of our favorite movies.

var favorites = movies.Where(movie => movie.Rating > 4.5)
                      .Select(movie => movie.Name);

foreach (var name in favorites)
{
    Console.WriteLine(name);
}
// Output:
// The Fifth Element
// Terminator 2
// Avatar
// My Neighbor Totoro

This time we wrote two nested LINQ methods. For every favorite movie, we only picked only its name. Here, the “mapping” function was the delegate: movie => movie.Name.

For more readability, we often align the nested LINQ methods vertically by the (.) dot.

2. Any

Any checks if a collection is empty or has at least one element matching a condition. It doesn’t return a new collection, but either true or false.

Let’s see if we have watched movies with a low rating.

var hasAnyMovies = movies.Any();
// true

var hasBadMovies = movies.Any(movie => movie.Rating < 2);
// false

3. All

Unlike Any, All checks if every element inside a collection matches a condition. It also returns either true or false instead of a new collection.

Let’s see if we have only watched really-good movies.

var weHaveSeenReallyGoodMovies = movies.All(movie => movie.Rating >= 4.5);
// false

4. GroupBy

GroupBy groups the elements of a collection based on a key. It returns a collection of “groups” or “buckets” organized by a key.

Let’s group our movies by rating.

var groupedByRating = movies.GroupBy(movie => movie.Rating);

foreach (var group in groupedByRating)
{
    Console.WriteLine($"Rating: {group.Key}");

    foreach (var movie in group)
    {
        Console.WriteLine($"{movie.Name}");
    }
    Console.WriteLine();
}
// Output:
// Rating: 4.5
// Titanic
//
// Rating: 4.6
// The Fifth Element
//
// Rating: 4.7
// Terminator 2
//
// Rating: 5
// Avatar
// My Neighbor Totoro
//
// Rating: 4
// Platoon

We grouped our list of movies using only one property: Rating. But, GroupBy has other use-cases: transforming each group and grouping by more than one property.

5. First and FirstOrDefault

First and FirstOrDefault return the first element in a collection or the first one matching a condition. First throws an exception if the collection is empty or doesn’t have matching elements. And FirstOrDefault returns the default value of the element type, instead.

Let’s find the oldest film we have watched.

var oldest = movies.OrderBy(movie => movie.ReleaseYear)
                   .First();
// Platoon

Here we first used OrderBy() to sort the movie collection by release year and then picked the first one.

In the same spirit of First() and FirstOrDefault(), we have Last() and LastOrDefault(). They return the last element instead of the first one.

.NET6 introduced new LINQ methods and oveloads. FirstOrDefault() and similar XOrDefault() methods have a new overload to pass an optional default value. And, we have methods like MinBy() we can use to replace an OrderBy() followed by First().

If we peek into the source code of DistinctBy, one of those new LINQ methods, we will see it’s not that intimidating after all.

5. Cheatsheet

There are more LINQ methods than the ones we’ve seen so far. These are some of them.

Method Function
Where Filter a collection
Select Transform every element of a collection
Any Check if a collection is empty
All Check if every element satisfies a condition
Count Count all elements of a collection
Distinct Find the unique elements of a collection
GroupBy Group the elements of a collection based on a key
OrderBy Sort a collection based on a key
First Find the first element of a collection. Throw if the collection is empty
FirstOrDefault Same as First but it returns a default value if it’s empty
Last Find the last element of a collection. Throw if the collection is empty
LastOrDefault It returns a default value if it’s empty, instead
Single Find only one element in a collection matching a condition. Throw, otherwise
SingleOrDefault It returns a default value if there isn’t one matching element, instead
Take Pick the first n consecutive elements of a collection
TakeWhile Pick the first consecutive elements that satisfy a condition
Skip Return a collection without the first n consecutive elements
SkipWhile Return a collection without the first consecutive elements that satisfy a condition
Sum Sum the elements of a collection
Min, Max Find the smallest and largest element of a collection
ToDictionary Convert a collection into a dictionary

That isn’t an exhaustive list. Of course, LINQ has more methods we don’t use often, like Aggregate and Intersect, Union, and Except. They’re helpful from time to time.

Popcorn
Speaking of movies. Photo by Christian Wiediger on Unsplash

6. LINQ Method syntax vs Query syntax

Up to this point, we have seen LINQ as extension methods on top of the IEnumerable type. But, LINQ has a language-level query syntax too.

Let’s find our favorite movies using language-level query syntax this time. Like this,

var bestOfAll = from movie in movies
                where movie.Rating > 4.5
                select movie;

It looks like SQL, isn’t it?

And this is the same code using extension methods,

var bestOfAll = movies.Where(movie => movie.Rating > 4.5);

We can use any of the two! But let’s favor the syntax used in the codebase we’re working with. If our code uses extension methods on IEnumerable, let’s continue to do that.

But there is one advantage of using query syntax over extension methods: we can create intermediate variables with the let keyword.

Find large files on the Desktop folder

Let’s find all files inside our Desktop folder larger than 10MB. And, let’s use let to create a variable. Like this,

var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
var desktop = new DirectoryInfo(desktopPath);

var largeFiles = from file in desktop.GetFiles()
                 let sizeInMb = file.Length / 1024 / 1024
                 //  ^^^^^
                 // We can create intermediate variables with 'let'
                 where sizeInMb > 10
                 select file.Name;

foreach (var file in largeFiles)
{
    Console.WriteLine(file);
}

Console.ReadKey();

The Length property returns the file size in bytes. We declared an intermediate variable to convert it to megabytes. Like this,

let sizeInMb = file.Length / 1024 / 1024

That’s the advantage of using query syntax over extension methods when working with LINQ.

7. Three common LINQ mistakes

It’s easy to start using LINQ. But, it’s easy to use misuse some of its methods. Here are three common mistakes when using LINQ:

1. Write Count instead of Any

Let’s always prefer Any() over Count() to check if a collection has elements or an element that meets a condition.

Let’s do,

movies.Any()

Instead of,

movies.Count() > 0

2. Write Where followed by Any

Let’s use a condition with Any() instead of filtering first with Where() to later use Any().

Let’s do,

movies.Any(movie => movie.Rating == 5)

Instead of,

movies.Where(movie => movie.Rating == 5).Any()

The same applies to the Where() method followed by FirstOrDefault(), Count(), or any other method that receives a filter condition.

3. Use FirstOrDefault without null checking

Let’s always check the result when working with FirstOrDefault(), LastOrDefault(), and SingleOrDefault(). If there isn’t one, they will return the default value of the collection type.

// We don't have movies with a rating lower than 2.0
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);

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);

For objects, the default value would be a null reference. And do you know what happens when we try to access a property or method on a null reference?… Yes, it throws NullReferenceException.

To make sure we always have a non-nullable result when working with FirstOrDefault(), let’s use the the DefaultIfEmpty method. It returns a new collection with a default value if the input collection is empty.

var worst = movies.Where(movie => movie.Rating < 2)
                  .DefaultIfEmpty(new Movie("Catwoman", 2004, 3))
                  // ^^^^^
                  .First();

Also, by mistake, we forget about the difference between Single and First and LINQ lazy evaluation expecting LINQ queries to be cached. These two are more subtle mistakes we make when working with LINQ for the first time.

8. Conclusion

Voilà! That’s it, Alice. That’s all you need to know to start working with LINQ in your code in 15 minutes or less. I know! There are lots of methods. But you will get your back covered with five of the most common LINQ methods.

With LINQ, we write more compact and expressive code. The next time we need to write logic using loops, let’s give LINQ a try!

Want to write more expressive code for collections? Join my course, Getting Started with LINQ on Udemy! You'll learn from what LINQ is, to refactoring away from conditionals, and to new methods and overloads from recent .NET versions. Everything you need to know to start working productively with LINQ — in less than two hours.

Happy LINQ time!