JohnA
JohnA

Reputation: 636

How do I properly deal with null values and 'where' using LINQ

So I'm in a sticky situation where I have a collection of items of a certain model. For simplicity sake I've made a small model named CarModel. In the collection it will have x amount of CarModel where some of the objects have properties where the value is null.

As for right now, it will run the where LINQ statement just fine on the first 3 items because they are null because it can check whether null == null.. However, it can't check whether "Test" == null, hence why I'm currently getting the exception

System.ArgumentNullException: 'Value cannot be null. Parameter name: value'

on this line cars.Where(x => x.Name?.ToLower().Contains(car.Name?.ToLower()) == true).ToList();

I was thinking about making the Name property nullable to test if that would work but I'm tied to .NET Framework 4.0 and I'm not allowed to change it so I can't use nullable properties because that required C# 8.0 and the language version is tied to the framework so I'm on C# 7.3

using System.Collections.Generic;
using System.Linq;
using System;

class Program
{
    static void Main(string[] args)
    {
        var car = new CarModel();
        car.Name = null;
        
        var cars = new List<CarModel>();
        for (int i = 0; i < 3; i++)
        {
            cars.Add(new CarModel { Name = null });
        }

        for (int i = 0; i < 3; i++)
        {
            cars.Add(new CarModel { Name = "Test" });
        }

        var t = cars.Where(x => x.Name?.ToLower().Contains(car.Name?.ToLower()) == true).ToList();
    }
}

internal class CarModel
{
    public string Name { get; set; }

    public int Year { get; set; }
}

How do I properly filter if some properties might be null at times. The goal right there is to return a collection of 3 items that it found because it matches the conditions in the WHERE statement.

Upvotes: 0

Views: 3358

Answers (4)

qTzz
qTzz

Reputation: 126

Just filter out the nulls before using the contains, you can't do a contains on a null value.

var t = cars.Where(x => x.Name != null && x.Name.ToLower().Contains(car.Name)).ToList();

Upvotes: 1

ByungYong Kang
ByungYong Kang

Reputation: 181

The reason you get the exception error is because Contains method requires non-null argument.

so you should revise your code like below,

var t = cars.Where(x => x.Name?.ToLower().Contains(car.Name?.ToLower() ?? string.Empty) == true).ToList();

Upvotes: 0

Corey
Corey

Reputation: 16574

In the provided example car.Name is always null, but I assume that you've got an actual use case where that's not always true.

The exception you're getting is from the Contains method, which can't handle a null parameter. You'll need to add a check for that, either skipping the entire section (not shown) that deals with the matching list or initialize an empty list when car.Name is null. Something like:

var t = car.Name is null ? new List<CarModel>() : 
    cars.Where
    (
        x => x.Name?.Contains(car.Name, StringComparison.InvariantCultureIgnoreCase) == true
    ).ToList();

That's assuming that you don't want to match null with null (as Yong Shun's answer does).

To be honest though, it might make more sense to ensure that your CarModel object doesn't accept null name values. Required a name in the constructor, use a property setter that throws on null and so on. Depending on use case, of course.

Upvotes: 1

Yong Shun
Yong Shun

Reputation: 51240

Perhaps you can write the query with dealing with 2 conditions:

  1. When the input value is null, query Name with null.
  2. When the input value is not null, query Name with not null and perform a case-insensitive search.
  3. Conditions 1 & 2 with or (fulfill either one).
var t = cars.Where(x => (car.Name == null && x.Name == null)
    || (car.Name != null && x.Name != null && x.Name.ToLower().Contains(car.Name.ToLower()) == true))
    .ToList();

Sample program

Upvotes: 0

Related Questions