简体   繁体   中英

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.

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

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)

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

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

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

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;

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 )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM