Linq to objects when object is null VS Linq to SQL - c#

I have this Linq to object query:
var result = Users.Where(u => u.Address.Country.Code == 12)
I get an exception if the Address or the Country are null.
Why this query doesn't check if the address is null and just after that procced?
This way I won't need to write this terrible query:
var result = Users.Where(u => u.Address != null &&
u.Address.Country != null &&
u.Address.Country.Code == 12)
In Linq to SQL the first query wiil do the job(from other reasons of course).
Is the a way to avoid the "null checks" in linq to object?

Unfortunately, "null" is treated inconsistently in C# (and in many other programming languages). Nullable arithmetic is lifted. That is, if you do arithmetic on nullable integers, and none of the operands are null, then you get the normal answer, but if any of them are null, you get null. But the "member access" operator is not lifted; if you give a null operand to the member access "." operator, it throws an exception rather than returning a null value.
Were we designing a type system from scratch, we might say that all types are nullable, and that any expression that contains a null operand produces a null result regardless of the types of the operands. So calling a method with a null receiver or a null argument would produce a null result. That system makes a whole lot of sense, but obviously it is far too late for us to implement that now; millions upon millions of lines of code have been written that expects the current behaviour.
We have considered adding a "lifted" member access operator, perhaps notated .?. So you could then say where user.?Address.?Country.?Code == 12 and that would produce a nullable int that could then be compared to 12 as nullable ints normally are. However, this has never gotten past the "yeah, that might be nice in a future version" stage of the design process so I would not expect it any time soon.
UPDATE: The "Elvis" operator mentioned above was implemented in C# 6.0.

No, it is a null reference exception just like accessing var x = u.Address.Country.Code; would be a NullReferenceException.
You must always make sure what you are de-referencing is not null in LINQ to objects, as you would with any other code statements.
You can do this either using the && logic you have, or you could chain Where clauses as well (though this would contain more iterators and probably perform slower):
var result = Users.Where(u => u.Address != null)
.Where(u.Address.Country != null)
.Where(u.Address.Country.Code == 12);
I've seen some Maybe() extension methods written that let you do the sort of thing you want. Some people do/don't like these because they are extension methods that operate on a null reference. I'm not saying that's good or bad, just that some people feel that violates good OO-like behavior.
For example, you could create an extension method like:
public static class ObjectExtensions
{
// returns default if LHS is null
public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator)
where TInput : class
{
return (value != null) ? evaluator(value) : default(TResult);
}
// returns specified value if LHS is null
public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
where TInput : class
{
return (value != null) ? evaluator(value) : failureValue;
}
}
And then do:
var result = Users.Where(u => u.Maybe(x => x.Address)
.Maybe(x => x.Country)
.Maybe(x => x.Code) == 12);
Essentially, this just cascades the null down the chain (or default value in the case of a non-reference type).
UPDATE:
If you'd like to supply a non-default failure value (say Code is -1 if any part is null), you'd just pass the new failure value into Maybe():
// if you wanted to compare to zero, for example, but didn't want null
// to translate to zero, change the default in the final maybe to -1
var result = Users.Where(u => u.Maybe(x => x.Address)
.Maybe(x => x.Country)
.Maybe(x => x.Code, -1) == 0);
Like I said, this is just one of many solutions. Some people don't like being able to call extension methods from null reference types, but it is an option that some people tend to use to get around these null cascading issues.
Currently, however, there is not a null-safe de-reference operator built into C#, so you either live with the conditional null checks like you had before, chain your Where() statements so that they will filter out the null, or build something to let you cascade the null like the Maybe() methods above.

Related

Ternary operator in lambda expression C#

I am trying to put a conditional expression with in a lambda query as below
GetPeers(bool? isConnected = true)
{
dbContext
.peers
.Where(m => isConnected.HasValue ? m.IsConnected == isConnected.Value : true)
.ToList();
}
Above statement gives me Nullable object must have a value. exception.
I dont get it! What is wrong in my query?
What I want if isConnected is null, then it should be returning all the records otherwise on the basis of m.IsConnected == isConnected.Value,
This is an issue with how Entity Framework is translating the ternary expression, it is attempting to create a SQL parameter using the result of isConnected.Value but isConnected is null so it throws.
My preferred option is to only apply the predicate if there is actually a predicate to apply:
IQueryable<Peer> peers = dbContext.peers;
if(isConnected.HasValue)
{
var isConnectedValue = isConnected.Value;
peers = peers.Where(m => m.IsConnected == isConnectedValue);
}
return peers.ToList();
You can change this
m.IsConnected == isConnected.Value
to this
m.IsConnected == (isConnected.HasValue && isConnected.Value)
The problem is that isConnected doesn't have always a value. It's a bool?. So it can has a value or not (null). If it has a value, it would be either true/false. The problem arises, when it has no value. This is happening due to conditional operator. According to the documentation:
The conditional operator (?:) returns one of two values depending on
the value of a Boolean expression. Following is the syntax for the
conditional operator.
condition ? first_expression : second_expression;
Either the type of first_expression and second_expression must be the
same, or an implicit conversion must exist from one type to the other.
For the full documentation, please have a look here.

Why does EF not return any results when comparing null variable?

I'm having an issue selecting data in my data context in Entity Framework and I've narrowed it down to querying for null values. I have a method like this:
public void DoStuff(int? someInt)
{
var someData = dataContext.MyEntities.Where(x => x.SomeProperty == someInt);
// someData always yields no results if someInt is null, even though
// there are rows in the table that have null for that column.
}
The above method fails if someInt is null. But this line works:
var someData = dataContext.MyEntities.Where(x => x.SomeProperty == null);
Why do I get data in the second one but not the first one?
I guess, then, that it is generating and using a SQL query of the form that expects a non-null value:
where x.SomeProperty = #param
Instead of the SQL to show the c# null-equality semantic:
where x.SomeProperty is null
(the key point here being that in c#, null equals null; in ANSI-SQL, null neither equals null nor (confusingly) not-equals null - different syntax is need to test nulls)
I've seen LINQ-to-SQL do the same thing, and agree that it is counter-intuitive. The only suggestion I have is: test the candidate parameter for null yourself and do the constant/literal == null test instead. It would also probably be able to do the same by inspecting and expression tree and re-writing it, if you are into expression trees - but special-casing the null is simpler.
someInt isn't null on its own. someInt.Value would be null.

c# .net 4.0 : nullable input string parameter and the lambda expressions

I have a following method:
public IQueryable<Profile> FindAllProfiles(string CountryFrom, string CountryLoc)
{
return db.Profiles.Where(p => p.CountryFrom.CountryName.Equals(CountryFrom,StringComparison.OrdinalIgnoreCase));
}
Right now, it just filters by CountryFrom but I need to filter it by CountryLoc as well.
So how do I modify the Where filter?
Also, CountryFrom could be null or CountryLoc could be null. So how do I modify the method's signature to all the nullable input string parameter.
I know how to do this in SQL but I am not sure about lambda expressions or LINQ.
Thanks
Your question isn't actually LINQ-specific. You can put any boolean conditions inside the lambda that you want, such as:
p => p.CountryFrom.CountryName.Equals(...) && p.CountryLoc.CountryName.Equals(...)
To handle null values you can use the static string.Equals method instead, like this:
p => string.Equals(p.CountryFrom.CountryName, CountryName, StringComparison.OrdinalIgnoreCase)
If this starts getting too long you could always extract it into a method.
return db.Profiles
.Where(p => p.CountryFrom.CountryName.Equals(CountryFrom,
StringComparison.Ordinal.IgnoreCase) &&
p.CountryLoc.CountryName.Equals(CountryLoc,
StringComparison.Ordinal.IgnoreCase));
Sorry...there was no way to make that statement pretty.
Just remember you can use any boolean logic you'd like inside of the Where extension method. It's just as easy as it would be anywhere else.
Also, a string parameter can be null no matter what (it's a reference type) so you don't need to worry about making things nullable.
You'll want to use the logical and operator "&&" in your lambda expression to filter by both CountryFrom and CountryLoc. Ex
db.Profiles.Where(p => p.CountryFrom == CountryFrom && p.CountryLoc == CountryLoc);
In C# strings are reference types and therefore can be null. You can check for null values using
myString == null

linq where clause and count result in null exception

The code below works unless p.School.SchoolName turns out to be null, in which case it results in a NullReferenceException.
if (ExistingUsers.Where(p => p.StudentID == item.StaffID &&
p.School.SchoolName == item.SchoolID).Count() > 0)
{
// Do stuff.
}
ExistingUsers is a list of users:
public List<User> ExistingUsers;
Here is the relevant portion of the stacktrace:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Enumerable.WhereListIterator1.MoveNext()
at System.Linq.Enumerable.Count[TSource](IEnumerable1 source)
How should I handle this where clause?
Thanks very much in advance.
I suspect p.School is null, not SchoolName. Simply add a null check before accessing SchoolName. Also, use Any() to check if there are any results instead of Count() > 0 unless you're really in need of the count. This performs better since not all items are iterated if any exist.
var result = ExistingUsers.Where(p => p.StudentID == item.StaffID
&& p.School != null
&& p.School.SchoolName == item.SchoolID)
.Any();
if (result) { /* do something */ }
For all database nullable columns, we should either add null check or do simple comparision a == b instead of a.ToLower() == b.ToLower() or similar string operations.
My observation as below:
As they get iterated through Enumerable of LINQ Query for comparision against with input string/value, any null value (of database column) and operations on it would raise exception, but Enumerable becomes NULL, though query is not null.
In the case where you want to get the null value (all the student, with school or not) Use left join.
There are a good example on MSDN
If I remember correctly (not at my developer PC at the moment and can't check with Reflector), using the == operator results in calling the instance implementation string.Equals(string), not the static implementation String.Equals(string, string).
Assuming that your problem is due to SchoolName being null, as you suggest, try this:
if (ExistingUsers.Where(
p => p.StudentID == item.StaffID
&& String.Equals( p.School.SchoolName, item.SchoolID)).Count() > 0)
{
// Do stuff.
}
Of course, comments by other answers count as well:
Using Any() instead of Count() > 0 will generally perform better
If p.School is the null, you'll need an extra check
Hope this helps.

Where Predicates in LINQ

How can I specify conditions in Where predicates in LINQ without getting null reference exceptions. For instance, if q is an IQueryable how can I do like:
Expression<Func<ProductEntity,bool>> predicate = p => !search.CategoryId.HasValue || (search.CategoryId.HasValue && search.CategoryId == p.CategoryId);
var q2 = q.Where(predicate);
Here search is an object that holds possible search conditions that may or may not be set like search.CategoryId might not be set but if it is I want to get the products that are set by that condition.
When I do this I get null reference exceptions.
You can use the null-coalescing operator ?? to replace a possible null value with a default value. The following sets tries to match the search.Category if it exists or simply creates an "always true" expression. This will be optimized by any good Linq query provider (e.g. LinqToSql).
Expression<Func<ProductEntity,bool>> predicate = p => (search.CategoryId ?? p.CategoryId) == p.CategoryId);
var q2 = q.Where(predicate);
Another possibility would be to dynamically compose a query predicate using PredicateBuilder. That's the way I do it for searches with a similar pattern as you use:
var predicate = PredicateBuilder.True<Order>();
if (search.OrderId))
{
predicate = predicate.And(a => SqlMethods.Like(a.OrderID, search.OderID);
}
// ...
var results = q.Where(predicate);
Let's dissect the line:
Expression<Func<ProductEntity,bool> predicate = p => !search.CategoryId.HasValue
|| (search.CategoryId.HasValue && search.CategoryId == p.CategoryId)
var q2 = q.Where(predicate);
So how many ways can we get null problems?
search (your "captured" variable) could be null
p could be null, meaning there is a null in the list
you've handled the case of search.CategoryId being null (Nullable<T>)
but maybe p.CategoryId (the category on a record in the list) is null (Nullable<T>) - however, I'm not sure that this would cause a NullReferenceException
q (the list / source) could be null
So: out of 5 options you've eliminated 1; look at the other 4? There is also the definite possibility that the issue is caused by something invisible not shown in the code; for example the get could be:
public int? CategoryId {
get {return innerObject.CategoryId;}
}
and innerObject could be null; if you eliminate the other 4 (pretty easy to do), look at at this one as a last resort.

Categories

Resources