Linq where column == (null reference) not the same as column == null - c#

I came across a rather strange problem with linq-to-sql. In the following example,
var survey = (from s in dbContext.crmc_Surveys
where (s.crmc_Retail_Trade_Id == tradeId) && (s.State_.Equals(state))
select s).First();
If tradeId is null, it doesn't behave as if I had specified null specifically like this instead,
var survey = (from s in dbContext.crmc_Surveys
where (s.crmc_Retail_Trade_Id == null) && (s.State_.Equals(state))
select s).First();
Which is my desired behavior. In fact it doesn't return anything unless both values are non-null. I can't figure out how to accomplish this short of several different linq queries. Any ideas?

Change where (s.crmc_Retail_Trade_Id == tradeId)
to
where (s.crmc_Retail_Trade_Id == tradeId ||
(tradeId == null && s.crmc_Retail_Trade_Id == null))
Edit - based on this post by Brant Lamborn, it looks like the following would do what you want:
where (object.Equals(s.crmc_Retail_Trade_Id, tradeId))
The Null Semantics (LINQ to SQL) MSDN page links to some interesting info:
LINQ to SQL does not impose C# null or
Visual Basic nothing comparison
semantics on SQL. Comparison operators
are syntactically translated to their
SQL equivalents. The semantics reflect
SQL semantics as defined by server or
connection settings. Two null values
are considered unequal under default
SQL Server settings (although you can
change the settings to change the
semantics). Regardless, LINQ to SQL
does not consider server settings in
query translation.
A comparison with the literal null
(nothing) is translated to the
appropriate SQL version (is null or is
not null).
The value of null (nothing) in
collation is defined by SQL Server;
LINQ to SQL does not change the
collation.

Another option to solve this, as I ran across this problem as well.
where (tradeId == null ? s.crmc_Retail_Trade_Id == null : s.crmc_Retail_Trade_Id == tradeId)

Not sure on this one, but I suspect when linq-to-sql translates that to an sql query string you get a slightly different expression specifying null directly such that at some point you end up comparing NULL to itself, and NULL=NULL is defined to be false.

I am not familiar with Linq, however in general:
NULL represents a missing, unknown, or undefined value. Strictly speaking, a variable cannot equal NULL; low-lvel languages which provide this construct usually do so as a convenience because there is no easy alternative -- at a higher level it's usually better to rely on ISNULL, defined, or whatever features your language supplies.
One undefined variable is not equal to another undefined variable (and the same applies to NULL == NULL). Joe Celko has a good example of writing a query to find all people whose hair colour matches the colour of the vehicle they drive. Should this query match a bald man who walks everywhere?

It's better to make sp for this purpose because linq will perform iteration it takes while for your assistance if you are using linq.
var c = lstQ_Buffer.Where(q => (((semesterId == 0 || semesterId == null ? q.fkSemesterId == null : q.fkSemesterId == semesterId) && (subjectSpecialtyId == 0 || subjectSpecialtyId == null ? q.fkSubSpecialtyId == null : q.fkSubSpecialtyId == subSpecialtyId) && (subTopicId == 0 || subTopicId == null ? q.fkSubTopicId == null : q.fkSubTopicId == subTopicId)) && (q.fkProgramId == programId && q.fkYearId == yearId && q.fkCourse_ModuleId == courseModuleId && q.fkSubject_SpecialtyId == subjectSpecialtyId && q.fkTopicId == topicId && q.fkDifficultyLevelId == diffucultyLevelId))).ToList();

Related

How to skip conditions in "Where" clause with multiple conditions?

I"m working on a WinfForms/C# software for automotive key management that's has a SQL query thats searchs in the table like that:
DataAdapter = SetAdapter($"SELECT * FROM keylist WHERE MANUFACTOR LIKE #manufactor AND KEYTYPE LIKE #type AND SERVICETYPE LIKE #service AND USER_ID = #_user");
So in the begginig I used to use the query directly in the search function but as the project grew, it ended up leaving it without performance, because the query was called directly on the remote server. So I decided to move everything to a list in C # and do the search with lambdas functions.
This is the function with lambda expression:
public List<Key> SearchFilter(string manufactor, string type, string service)
{
return _keys.Where(key => key.Manufactor == manufactor
&& key.Type == type
&& key.ServiceType == service).ToList();
}
The problem is:
In the SQL syntax, when you leave one or more fields, it automatically ignores and checks for the other, but when I use Where <> in the LINQ for example, it ends up returning null or items that do not satisfy conditions and returning other objects.
When I leave one or two of the parameters null, it returns no value. By the way, if I use || instead of && it returns undesirable values.
Is there a way to check if the condition is null and skip to the next clause and return only the values that were passed?
if I understand correctly, I think I should make several "Where" conditions, one for each condition, that is:
return _keys.Where(key => manufactor != string.Empty ? key.Manufactor == manufactor ? key.Manufactor != string.Empty)
.Where(key => key.Type == type)
.Where(key => key.ServiceType == service)
.ToList();
You can also "exclude" or "include" filters if there is value to filter as I did with "key.Manufactor".

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.

Null reference exception being thrown in EF LINQ query if-clause

I couldn't find the exact words to explain what's happening, so if this is a duplicated question, I apologize.
I tried to do a quite simple AND condition if-clause inside a LINQ Query, in order to check if an object is null and then verify if its property is equal or not the column I wanted to compare.
The code:
public IEnumerable<Plan> GetPlans(Plan plan)
{
return _context.Plans.Where(e =>
e.Situation == plan.Situation &&
e.Notes.Contains(plan.Notes) &&
(plan.Excercise != null && plan.Exercise.Year > 0 ? e.Exercise.Year == plan.Exercise.Year: true)).ToList();
}
I've already done this kind of check a dozen times before in .NET 4.5, without having any kind of issue.
But now, in the first .NET Core 2.0 project I'm working on, I had the following error:
An exception was thrown while attempting to evaluate a LINQ query parameter expression. To show additional information call EnableSensitiveDataLogging() when overriding DbContext.OnConfiguring.
The inner exception is clearer: NULL REFERENCE EXCEPTION.
After some tests, I found out that the error happens when plan.Exercise comes null, even if I try to avoid the exception by checking at first if it's null or not.
If I try to do the same check directly in Immediate Window, it returns "false", as it should be.
Am I missing something here? It could be an EF bug? Any particular reason why this works in .NET 4.5, for example, and not in .NET Core 2.0?
Thanks in advance.
UPDATE
Ivan's solution did the job:
Rewrite ? : constructs with equivalent ||
plan.Excercise == null || plan.Exercise.Year <= 0 || e.Excercise.Year == plan.Exercise.Year
It sounds like this might be a bug in EF Core (but I don't know this for sure).
One thing you might try is to fail fast if the base requirements of plan are not met, and more importantly, instead of using the ternary operator, use the traditional comparison operators along with parenthesis:
public IEnumerable<Plan> GetPlans(Plan plan)
{
if (plan == null) return new List<Plan>();
return _context.Plans
.Where(e =>
e.Situation == plan.Situation &&
e.Notes.Contains(plan.Notes) &&
(plan.Exercise == null ||
plan.Exercise.Year <= 0 ||
e.Excercise.Year == plan.Exercise.Year))
.ToList();
}
To avoid this issue, make sure that you are not evaluating on null object.
var exercice = await _repositoryExercice.FirstOrDefaultAsync(i => i.IsCurrent);
var depenses = _repositoryDepense.GetAll()
.Where( e => e.ExerciceId.Equals(exercice.Id))
.WhereIf(AbpSession.TenantId.HasValue, m => m.TenantId.Value.Equals(AbpSession.TenantId.Value))
.ToList();
The issue was causing by this line .Where( e => e.ExerciceId.Equals(exercice.Id)) because the variable exercice is null.
Best practice, I replaced that line by this :
...
.WhereIf(exercice != null, e => e.ExerciceId.Equals(exercice.Id))
...
how about simplifying your code into something like
public IEnumerable<Plan> GetPlans(int year)
{
return _context.Plans
.Where(e => e.Excercise.Year == year)
.ToList();
}

How to simplify complicated conditional clauses when using LINQ to SQL

Complicated statements with lots of AND/OR compenents are horrible to read and prone to errors - in a normal IF statement I might make use of a method call to simplify the is statement - for example:
if (((user == myUser || user == yourUser) && user != Admin)
&& Something > SomethingElse
&& (thresholdDate > item.itemDate || (item.itemDate == null && item.itemType == itemIsDated))
)
{
DoStuff();
}
I could refactor out the user and date parts to make things easier to read:
if (
UserValid(user)
&& Something > SomethingElse
&& DateIsValid(thresholdDate, item)
)
{
DoStuff();
}
What can I do in a LINQ query to simplifiy the nested IF?
For example if I have something along the lines of:
var someResults = DataManager.Things
.Where(item => (item.UserName == currentUser.UserName
|| item.ParentUsername == currentUser.UserName)
&& (item.ItemType == (int) ItemType.MyType
|| item.ItemType == (int) ItemType.YourType)
&& item.Result == null
&& (
(item.Status == null
&& (item.ItemDate < thresholdDate
|| item.ItemType == (int) ItemType.YourType)
)
||
(item.Status != null &&
item.Status != "Rejected")
)
)
** not actual code - just a simplified and generic example.
I'd like to be able to extract parts of the logic into methods - or in some other way seperate out the AND / OR mess such that it's clear what is going on.
I've tried adding a method to the 'item' to perfrom some of the logic a kind of IsValidType(typeOptions) method - this compiles fine but LINQ complains that it does't recognise the method at runtime.
I could use a property on the item - but then I can't pass any context information (which makes it of limited use)
How do you go about making this kind of query readable?
As Tim mentioned in a comment, you can use methods in most LINQ-to-something implementations.
The result would look like this:
.Where(item => CheckUserName(item)
&& CheckItemType(item)
&& CheckItemResult(item)
&& CheckItemStatus(item));
In case of LINQ to SQL, LINQ to Entities or other remote-executed implementations, you can at least take advantage of query optimization and rewrite all && to separate Where calls, because they are equivalent:
.Where(item => item.UserName == currentUser.UserName
|| item.ParentUsername == currentUser.UserName)
.Where(item => item.ItemType == (int) ItemType.MyType
|| item.ItemType == (int) ItemType.YourType)
.Where(item => item.Result == null)
.Where(item => (item.Status == null
&& (item.ItemDate < thresholdDate
|| item.ItemType == (int) ItemType.YourType))
|| (item.Status != null
&& item.Status != "Rejected"));
The resulting query will join the where clauses into one enumerator.
I assume you can't do it directly in your code. Of course if you had plenty of similar conditions, you could generate the expression for them dynamically. But you can do very little with single big Where condition here not hurting the performance =(.
If you consider it more appripriate, you could split your single big condition to few consequent Where() calls. LINQ tree when converting to SQL will merge conditions and generate single select. DBMS can also optimize the generated SQL for maximum efficiency. So you can in some cases intentionally write not optimized queries for better readability and rely on automatic optimizations. Though be careful with it and check if your specific conditions really gets merged and optimized as you expect.
If you can move this code to SQL stored procedure, you could configure LINQ to SQL to call this procedure when you need. That will simplify the calling code, but move complications to SQL. I suppose you can even extract some of your conditions to SQL functions and make them being called, using in the Where clause. Though to be honest I never used it - just read about possibility. For more details and example look at this article: http://weblogs.asp.net/zeeshanhirani/archive/2008/05/21/table-valued-functions-in-linq-to-sql.aspx
Three very different options:
Use Resharper. It contains a number of code inspections and refactorings to reduce if statements or point out redundant predicates.
In linq-to-sql you can skip most check for null. That is because the expressions are translated into SQL and in SQL there is no null reference exception. A linq (to sql) clause like where a.Name == "x" just yields false when there is no a. In linq-to-objects this would throw a null reference exception.
For comparisons that are done really often you may consider adding computed columns to your database tables. Like a column IsRejected that evaluates Status = 'Rejected'. In linq that would reduce to where !item.IsRejected.

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