2

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.

1
  • 1
    LINQ expressions are no different than regular boolean expressions, so checking for null is just the same. Commented May 5, 2017 at 13:37

6 Answers 6

5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I like this answer the best for its completeness, even though I don't really care about cultures. The only thing is that Reshaper suggests that you merge the last two sequential checks into: (!string.IsNullOrEmpty(x.Claim?.InitiatedBy) && x.Claim.InitiatedBy.ToLowerInvariant().Contains(searchString))
I agree with the almighty Resharper on this.
I would probably extract a method so as not to repeat the calls to string.IsNullOrEmpty() and .ToLowerInvariant().Contains() and to cover the case in which the test criteria changes. Depending on the domain, one could even opt to make the object itself expose said method if appropriate to further reduce possible duplication.
5

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

4 Comments

You could try something like this. It will allow both null and non null. (string.Equals(x.ContractNum, string.Empty) || x.ContractNum.Trim().Contains("searchString"))
@JeanB Sure, there's a few different ways to tackle it. Feel free to add a different answer.
My mind really has been over-taxed lately and I'm not sure why I just didn't do this/remember how to do this. I kept trying to put ? all over the place and it keep giving me errors. Such a simple fix.
@BarryFranklin No worries - often I think there should be a slick operator or trick to do something that in reality is pretty simple.
4

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)

3 Comments

What would be the benefit of your suggestion to use IndexOf?
I like the example which uses IndexOf, however I think that the operator should be >= as the result of IndexOf is zero-based.Using > would miss the case where the search string happens to be at the start of the searched string.
1

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

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

5 Comments

Please no. Don't do that. Just do regular null checking.
What would the difference be?
Useless ToLower() and subsequent calls.
Wouldn't this return a lower case empty string if null? How does a lower case empty string look? :)
@BarryFranklin, I think what Patrick Hofman meant is that it's pointless to make an empty string lowercase and look for things in it. Although I would think that these library methods, that end up using extern functions probably implemented in C++, would optimize away the case in which they are fed an emtpy string and return straight away. Still, you would have several useless method calls. Hardly a nasty performance bottleneck on today's hardware, but sticking to null checking, however ugly, is avoids that.
1

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;

5 Comments

The last option is best IMHO.
This would work only for C# 6+. But that is a good option too :)
@PatrickHofman I usually prefer the last too as == true and equivalent reads wrong to me.
The first works, but the second one doesn't work for me - I think something having to do with the ||. What is that supposed to do? what does something ?? false mean?
@BarryFranklin It is the null coalescing operator or essentially if LHS is null return the RHS
1

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 )

5 Comments

Constructing a new array is pointless, loses readability in my opinion and probably takes longer. Also I believe it should be Select not Where.
@TheLethalCoder Readability can be debatable, but instead of repeating the logic for each separate property this keeps it in one place. Imho increasing the readability. (and makes sure one can't forget the ? for one property) At any rate , Where is correct: the array is only used for the Any using it and short circuits on a match
Regarding performance: yes, this is heavier in milliseconds on greater lists, but if that's a problem I'd create some sort of iterator and still keep the checking logic in one place
Oh I misread the I thought the Any was outside of the Where not in it.
This one may work just fine (I haven't tested), but it confuses me a bit and I'm sure it wouldn't be clear to anyone else who would read my code later. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.