Null value in linq where clause - c#

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

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 )

Related

C# LINQ How to efficiently deal with a null return?

So when I'm writing C# linq statements I often would like to do something like this:
string name = database.table.Where(t=>t.Field1 == row.Field1 && t.Field2 == row.Field).Select(b=> b.Name).FirstOrDefault().ToString();
However if the statement above doesn't return anything then it errors because you can't use .FirstOrDefault() on a null value.
So what I usually end up doing instead is something like below:
var listOfNames = database.table.Where(t=>t.Field1 == row.Field1 && t.Field2 == row.Field).Select(b=> b.Name).ToList();
if(listOfNames.Count() > 0 ){
string name = listOfNames.FirstOrDefault().ToString();
}
The code above just seems really verbose and I have to imagine there is a much cleaner way of doing this while dealing with a potential null return value.
Any insight or help would be appreciated. Thanks.
You have a few ways:
do not use ToString() I guess Name is string/varchar already
string name = database.table
.Where(t=>t.Field1 == row.Field1 && t.Field2 == row.Field)
.Select(b=> b.Name)
.FirstOrDefault()
// name now can be null since default(string) is null
if you need to do ToString then you can use C# 6 features – Null-conditional (?.)
string name = database.table
.Where(t=>t.Field1 == row.Field1 && t.Field2 == row.Field)
.Select(b=> b.Name)
.FirstOrDefault()?.ToString(); // here ?
// name now can be null
also consider null-coalescing (??) operator
string name = database.table
.Where(t=>t.Field1 == row.Field1 && t.Field2 == row.Field)
.Select(b=> b.Name)
.FirstOrDefault() ?? "some value"
// so if FirstOrDefault returns null then "some value" will be used
Combine the .Where and .FirstOrDefault:
string name = database.table
.FirstOrDefault(t=>t.Field1 == row.Field1 && t.Field2 == row.Field)
?.Name;

Null value is still evaluated after testing it with condition

I have the following LINQ query:
houses.Where(x =>
x.A && user.B
||
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode
))
I get an error when user.Address is null in:
user.Address.CountryCode
To avoid that I tried to use:
x.Address == null || user.Address == null
But it seems user.Address.CountryCode is still evaluated.
How can I avoid that?
It's definitely EF Core 3.0 query translator defect.
For now (and in general) you can avoid it by not using conditional operator ?: for criteria expressions which need to be converted to SQL query parameters (like your user.Address.CountryCode), but the equivalent logical binary expressions.
e.g. instead of
(x.Address == null || user.Address == null ? false : x.Address.CountryCode == user.Address.CountryCode)
use the equivalent
(x.Address != null && user.Address != null && x.Address.CountryCode == user.Address.CountryCode)
Personally I find this logic confusing. You might try:
( (x?.Address?.CountryCode != null && user?.Address?.CountryCode != null) ? x.Address.CountryCode == user.Address.CountryCode : false
If that doesn't work then there is something very odd going on with the Address or CountryCode properties! Probably and entity framework issue.

LINQ query needed using a conditional operand in the WHERE clause

I am having trouble composing a LINQ statement that uses a conditional operand within the where clause and have looked through this site for a similar problem without any luck. I'm sure there is a better way to express this query. The value XXX within the is the result of determining which field in the object to use for comparison and is explained below.
Below is what I am trying to accomplish:
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& XXX >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();
Value XXX =
if registerEntry.AddedDate = registerEntry.PostDate
registerEntry.AddedDate
else
registerEntry.PostDate
The values registerEntry.AddedDate and registerEntry.PostDate are DateTime type, however when comparing I need to only compare the date and not the time
Does anyone have any idea on how to do this?
Why not just use registerEntry.PostDate for XXX? If it is equal to registerEntry.AddedDate, might as well use it and simplify the whole thing
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& registerEntry.PostDate >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();
Depending on the LINQ provider you're using you can try the ternary operator:
var paymentReceivedAmt = (from registerEntry in RegisterEntries
where registerEntry.TransactionType != null
&& registerEntry.TransactionType.Id.SubsystemCode == "A"
&& registerEntry.Receipt != null
&& registerEntry.PostDate != null
&& (registerEntry.AddedDate == registerEntry.PostDate
? registerEntry.AddedDate
: registerEntry.PostDate) >= lastInvoicedDate
select registerEntry.TransactionAmount.GetValueOrDefault(0)).Sum();

more short code about if statement

i wanted to try the following code:
//all arrays are List<T> type.
if (m.terms[0] != null && m.terms[0].labels != null && m.terms[0].labels[0].title == "Part-of-speech")
{
result = true;
}
but it occured runtime error occasionly in following situation
i. m.terms == null
ii. m.terms != null, but m.terms[0] does not intialized.
iii. m.terms != null, and m.terms[0] has been exist but
m.terms[0].label does not initialized.
...
so i did modify it to like this:
if (m.terms[0] != null)
{
if (m.terms[0].labels != null)
{
if (m.terms[0].labels[0].title == "Part-of-speech") { result = true; }
}
}
is it the best way?
&& is a short circuiting operator, so the first way you wrote it and the second way will be functionally equivalent.
if (a && b && c)
{
// work
}
b will only be evaluated if a returns true. (Same goes for c).
In your code, checking m.terms[0].labels will not be a problem because you would have short-circuited out of the expression if m.terms[0] had been null.
To completely cover yourself, you'd want to possibly add checks for m and m.terms, however.
m != null && m.terms != null && m.terms.Count > 0 && m.terms[0] != null ...
As it evaluates from left to right, it will break on the first condition that doesn't pass and the rest will go unchecked.
int index = 0;
int labelIndex = 0;
string titleToCheck = "Part-of-speech";
if (m != null && m.terms != null && m.terms.Count > index)// or m.Length...
{
if (m.terms[index] != null && m.terms[index].labels != null &&
m.terms[index].labels.Count > labelIndex)
{
if (m.terms[index].labels[labelIndex].title == titleToCheck)
{
result = true;
}
}
}
This is all about readability. C# uses Short-circuit evaluation so in functionality there is no difference.
try this
if (m!=null && m.terms!= null && m.terms[0].labels!=null && m.terms[0].labels[0].title!=null && m.terms[0].labels[0].title == "Part-of-speech")
Yes, it would be better to split off each null check into a separate if statement.
The reason is that the second and third conditions require the first to not be null. If the first is null, then the second and third conditions will in turn throw errors because their parent is null yet is trying to be accessed.

LINQ to SQL and Null strings, how do I use Contains?

Here is the query
from a in this._addresses
where a.Street.Contains(street) || a.StreetAdditional.Contains(streetAdditional)
select a).ToList<Address>()
if both properties in the where clause have values this works fine, but if for example, a.StreetAdditional is null (Most of the times), I will get a null reference exception.
Is there a work around this?
I'd use the null-coalescing operator...
(from a in this._addresses
where (a.Street ?? "").Contains(street) || (a.StreetAdditional ?? "").Contains(streetAdditional)
select a).ToList<Address>()
The most obvious one:
from a in this._addresses
where (a.Street != null && a.Street.Contains(street)) || (a.StreetAdditional != null && a.StreetAdditional.Contains(streetAdditional))
select a).ToList<Address>()
Alternatively you could write an extension method for Contains that accepts a null argument without error. Some might say that it is not so pretty to have such a method, because it looks like a normal method call, but is allowed for null values (thereby setting aside normal null-checking practices).
You must check first if StreetAdditional is null.
Try
where a.Street.Contains(street) || ((a != null) && a.StreetAdditional.Contains(streetAdditional))
This works because && is a shortcut-operator and if a != null yields false, the second expression with the null-value won't be evaluated since the result will be false anyway.
I would create an extension method to return an empty sequence if null and then call contains method.
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> pSeq)
{
return pSeq ?? Enumerable.Empty<T>();
}
from a in this._addresses
where a.Street.Contains(street) ||
a.StreetAdditional.EmptyIfNull().Contains(streetAdditional)
select a).ToList<Address>()
I don't think SqlServer gave you a null exception. If it did, then this code is clearly not running though LinqToSql (as you've tagged the question).
string.Contains would be translated to sql's like, which has no trouble at all with null values.
Check to make sure that the properties are not null
from a in this._addresses
where (a.Street != null && a.Street.Contains(street)) ||
(a.StreetAdditional != null && a.StreetAdditional.Contains(streetAdditional))
select a).ToList<Address>()
If the null check is false, then the second clause after the && will not evaluate.
from a in this._addresses
where a.Street.Contains(street) || (a.StreetAdditional != null && a.StreetAdditional.Contains(streetAdditional)
select a).ToList<Address>()
You might want to check to make sure the variables street and streetAdditional are not null. I just ran across the same problem and setting them to an empty string seemed to solve my problem.
street = street ?? "";
streetAdditional = streetAdditional ?? "";
from a in this._addresses
where a.Street.Contains(street) || a.StreetAdditional.Contains(streetAdditional)
select a).ToList<Address>()
One thing to note is that the null should be evaluated first.
where (**a.Street != null** && a.Street.Contains(street)) || (a.StreetAdditional != null && a.StreetAdditional.Contains(streetAdditional))
select a).ToList<Address>
()

Categories

Resources