Barry Franklin
Barry Franklin

Reputation: 1820

Null value in linq where clause

I'm having an issue where I want to return results where something matches and I get an error if one of the properties I'm trying to match is null.

  if (!string.IsNullOrEmpty(searchString))
  {
      Infos = Infos.Where(
          x =>
          x.FirstName.ToLower().Contains(searchString) ||
          x.LastName.ToLower().Contains(searchString) ||
          x.ContractNum.ToLower().Contains(searchString) ||
          x.VIN.ToLower().Contains(searchString) ||
          x.Claim.InitiatedBy.ToLower().Contains(searchString)
          ).ToList();
  }

If ContractNum or VIN, for example, are null then it throws an error. I'm not sure how to check if one of these are null inside of a linq query.

Upvotes: 2

Views: 25796

Answers (6)

Habib
Habib

Reputation: 223237

You have multiple options, first is to do an explicit check against null and the other option is to use Null propagation operator.

x.FirstName != null &&  x.FirstName.ToLower().Contains(searchString)

or

x.FirstName?.ToLower()?.Contains(searchString) == true

But I would suggest you to use IndexOf instead of Contains for case insensitive comparison.

something like:

x.FirstName?.IndexOf(searchString, StringComparison.CurrentCultureIgnoreCase) >= 0)

Upvotes: 4

Me.Name
Me.Name

Reputation: 12544

An alternative method to keep the comparison logic in one place to use a sub collection of the properties and check on those:

Infos = Infos.Where(i=> 
   new[] {i.FirstName,i.LastName,i.ContractNum /*etc*/}
   .Any(w=> w?.ToLower().Contains(searchString) ?? false))
   .ToList();

(It does read out all properties, but that shouldn't cost much performance and gains much maintainability )

Upvotes: 1

Muffun
Muffun

Reputation: 736

Checking the property is null or empty before comparing it it's the only way I know

if (!string.IsNullOrEmpty(searchString))
      {
          Infos = Infos.Where(
              x =>
              (!String.IsNullOrEmpty(x.FirstName) && x.FirstName.ToLowerInvariant().Contains(searchString)) ||
              (!String.IsNullOrEmpty(x.LastName) && x.LastName.ToLowerInvariant().Contains(searchString)) ||
              (!String.IsNullOrEmpty(x.ContractNum) && x.ContractNum.ToLowerInvariant().Contains(searchString)) ||
              (!String.IsNullOrEmpty(x.VIN) && x.VIN.ToLowerInvariant().Contains(searchString)) ||
              (x.Claim != null && !String.IsNullOrEmpty(x.Claim.InitiatedBy) && x.Claim.InitiatedBy.ToLowerInvariant().Contains(searchString))
              ).ToList();
      }

EXTRA: I added a check on the Claim property to make sure it's not null when looking at InitiatedBy

EXTRA 2: Using the build in function IsNullOrEmpty to compare string to "" and nullso the code is clearer.

Extra 3: Used of ToLowerInvariant (https://msdn.microsoft.com/en-us/library/system.string.tolowerinvariant(v=vs.110).aspx) so the lowering action will act the same no matter of the culture.

Upvotes: 5

TheLethalCoder
TheLethalCoder

Reputation: 6744

I would use the null conditional operator ?, this will however, return a nullable bool? so you will need to handle that appropriately.

Some examples on how to do this:

x?.FirstName?.ToLower().Contains(searchString) == true;
x?.FirstName?.ToLower().Contains(searchString) ?? false;

Upvotes: 1

ThatChris
ThatChris

Reputation: 752

You could use ?? to replace it with a acceptable value.

   (x.ContractNum??"").ToLower()

Upvotes: 1

D Stanley
D Stanley

Reputation: 152521

You can add explicit null checks:

  Infos = Infos.Where(
      x =>
      (x.FirstName != null   && x.FirstName.ToLower().Contains(searchString)) ||
      (x.LastName != null    && x.LastName.ToLower().Contains(searchString)) ||
      (x.ContractNum != null && x.ContractNum.ToLower().Contains(searchString)) ||
      (x.VIN != null         && x.VIN.ToLower().Contains(searchString)) ||
      (x.Claim != null       && x.Claim.InitiatedBy != null && x.Claim.InitiatedBy.ToLower().Contains(searchString))
      ).ToList();

Upvotes: 5

Related Questions