LINQ to Entities does not recognize the method IsNullOrWhiteSpace - c#

I have the below code:
var countries = from c in db.Countries
where (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha2 ?? string.Empty).ToUpper().Contains(searchAlpha2.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchAlpha2) || (c.Alpha3 ?? string.Empty).ToUpper().Contains(searchAlpha3.ToUpper()))
&& (string.IsNullOrWhiteSpace(searchName) || (c.Name ?? string.Empty).ToUpper().Contains(searchName.ToUpper()))
select c;
This code uses Entity Framework v6 Code First over a SQL database.
Aside from performance, if I don't include the IsNullOrWhitespace I get no results when the filter criteria are blank (I've tested both null and blank values); however when a value is present this works as expected.
I'm getting the error:
LINQ to Entities does not recognize the method 'Boolean IsNullOrWhiteSpace(System.String)' method, and this method cannot be translated into a store expression.
I'm trying to use the searchXXX strings to filter on columns. I've tried using RegEx.IsMatch, SqlMethods.Like, and the code below, but all give me errors saying those functions are not allowed (errors come from either EntityFramework.SqlServer or from Linq to Entities). I've seen numerous posts on here where this has been done successfully though - so wonder if I'm missing something fundamental?

If you want to use your statement in current form you might want to replace
string.IsNullOrWhiteSpace(searchAlpha2)
to
!(searchAlpha2 == null || searchAlpha2.Trim() == string.Empty)
and all the other values too, for it to get translated to working SQL.
Update: Copied from comment by #DavidKempfner
As of EntityFramework.6.2.0 it generated SQL that checked for
!(searchAlpha2.Trim() == string.Empty),
I.E. It ignored the searchAlpha2 == null || part.
Use this instead:
!string.IsNullOrEmpty(entity.searchAlpha2.Trim())

I would suggest a different approach - use the ability to build queries up on the fly, and thus avoid passing optional query parameters to the expressions altogether - this will result in improved query plans when parsed to sql and executed on the database.
Also, if your database (?SqlServer) is not set to case sensitive collation (i.e. xx_CI_xx), you can avoid the casing conversion as well, as it is redundant:
var myQueryable = db.Countries.AsQueryable();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
{
myQueryable = myQueryable.Where(c => c.Alpha2.Contains(searchAlpha2));
}
...
var countries = myQueryable.ToList();
You can get this and a bunch more functionality using PredicateBuilder
Update
JB: based on StuartLC's answer, here's the code amended to use PredicateBuilder:
var predicate = PredicateBuilder.True<Country>();
if (!string.IsNullOrWhiteSpace(searchAlpha2))
predicate = predicate.And(c => c.Alpha2 != null ? c.Alpha2.Contains(searchAlpha2) : false);
if (!string.IsNullOrWhiteSpace(searchAlpha3))
predicate = predicate.And(c => c.Alpha3 != null ? c.Alpha3.Contains(searchAlpha3) : false);
if (!string.IsNullOrWhiteSpace(searchName))
predicate = predicate.And(c => c.Name != null ? c.Name.Contains(searchName) : false);
IQueryable<Country> countries = db.Countries.AsExpandable().Where(predicate);

when you use linq in Entity Framework to get data from DataBase you need to use only with functions that the Entity Framework can convert to sql query.

I know that there an already accepted answer for this question but I got an idea to share.
Instead of putting multiple checks in the LINQ or using if statement to apply where clause on list result, you can use a simple trick. Just declare a bool variable and assign IsNullOrWhitespace of add to linq like:
bool isNull = string.IsNullOrWhiteSpace(searchAlpha2);
var countries = db.Countries.Where(c => isNull || c.Alpha2.Contains(searchAlpha2)).ToList();

Related

How can I query a database with LINQ when the column is a number but I have a string in the WHERE?

I am using this code:
query = String.IsNullOrEmpty(options.PhraseNum) ?
query :
query.Where(w => w.PhraseNum == Convert.ToInt32(options.PhraseNum));
However I get an error:
LINQ to Entities does not recognize the method 'Int32 ToInt32(System.String)' method, and this method cannot be translated into a store expression.
Is there a way I can do this in LINQ and if not how can I convert outside of this and have the conversion not cause an exception if the string is not null?
Only modify query if the string can be parsed to an int, which is an implicit check if it isn't null or empty:
if (int.TryParse(options.PhraseNum, out var phraseNum))
{
query = query.Where(w => w.PhraseNum == phraseNum);
}
Before C# 7 the syntax was
int phraseNum;
if (int.TryParse(options.PhraseNum, out phraseNum))
etc.
It looks like LINQ is trying to evaluate the expression Convert.ToInt32(options.PhraseNum) on the server side as part of the WHERE clause.
Redo the code so that the variable is cast explicitly on the client-side outside the query expression:
Int32 phrase_num = String.IsNullOrEmpty(options.PhraseNum) ? 0 : Convert.ToInt32;
query = String.IsNullOrEmpty(options.PhraseNum) ? query : query.Where(w => w.PhraseNum == phrase_num);
Or for a tidier approach overall:
if(!String.IsNullOrEmpty(options.PhraseNum))
{
Int32 phrase_num = Convert.ToInt32(options.PhraseNum);
query = query.Where(w => w.PhraseNum == phrase_num);
}
I think that should resolve the problem and preserve the intended program logic.
EDIT: I'd endorse Gert Arnold's approach above if you are using the latest C# version, where out parameters can be declared inline in the TryParse method call itself.

LINQ nested array and the ternary operator. The nested query is not supported. Operation1='Case' Operation2='Collect'

The following code produces the error
The nested query is not supported. Operation1='Case' Operation2='Collect'
The question is what am i doing so terribly wrong? How can i fix that?
IQueryable<Map.League> v = from ul in userLeagues
select new Map.League
{
id = ul.LeagueID,
seasons =
inc.Seasons ? (from ss in ul.Standings
where ss.LeagueID == ul.LeagueID
select new Map.Season
{
seasonId = ss.Season.SeasonId,
seasonName = ss.Season.SeasonName
}).ToList() : null,
};
Update
what i cannot follow is why this is working as a charm
seasons = (from ss in ul.Standings
where ss.LeagueID == ul.LeagueID
select new Map.Season
{
seasonId = ss.Season.SeasonId,
seasonName = ss.Season.SeasonName
}).Distinct(),
what is wrong with the ternary operator?
The exception indicates that you're using Entity Framework. Always good to mention the LINQ implementation in questions.
When LINQ runs against a SQL backend, the SQL provider tries to translate the whole statement into one SQL statement. This greatly reduces the types of operations that are supported, because SQL is far more restricted than LINQ. Note that the variable inc.Seasons should also part of the SQL statement. Now the problem is that a SQL can't return two different result set depending on a variable that's part of itself: there is always one fixed SELECT clause.
So there is a Case method in the expression in a place where it's not supported (and I guess that hence the subsequent Collect isn't supported either).
You could solve this by making the inclusion part of the where clause:
from ul in userLeagues
select new Map.League
{
id = ul.LeagueID,
seasons = from ss in ul.Standings
where inc.Seasons // here
&& ss.LeagueID == ul.LeagueID
select new Map.Season
{
seasonId = ss.Season.SeasonId,
seasonName = ss.Season.SeasonName
})
}
I think you simply can't put an if-else inside a linq query, at least not in that spot.
See this post for detailed explanation and discussion.
Oh, and specially look at the "hack" by user AyCabron, I think that could resolve your situation neatly (depending on what you exactly want and why you choose to have null pointers).
The problem is not with Linq in general but with Linq to objects.
since you are using IQueryable you expect the query to run in the DB,
in that context, you cannot use many operators including the ternary operator.
if you tried the same code using Linq to objects (i.e Enumerable) it will succeed.
see example here: working example
Error The nested query is not supported. Operation1='Case' Operation2='Collect' is generated by EF when you use null within a ? statement.
EF can not convert statements like condition ? object/list : null.
In your specific example, remove .ToList() as it will also produce error when there is no rows return. EF will automatically give you null when there is no items to select.

Force equality operator when comparing to null in NHibernate

NHibernate is clever when it comes to comparing fields to null. Consider the following LINQ statement:
string s = null;
var rows = session.Query<Entity>
.Where(e => e.SomeField == s)
.ToList();
The statement is transformed into the SQL query
SELECT ...
FROM entity_table
WHERE some_field IS NULL;
In some cases I don't want this behavior. I want the resulting query to contain the some_field = NULL restriction (which is never true).
(How) can I instruct NHibernate to do this? Or do I have to write two queries and handle the null-case explicitly in code?
Note that this is in the context of NHibernate, not linq-to-sql. NHibernate cannot transform object.Equals(e.SomeField, s) into an SQL query. Also, comparing to a variable which is currently null and comparing to the null keyword directly will yield the same query with NHibernate.
You can use QueryOver, but will generate parameterized query:
string s = null;
var rows = session.QueryOver<Entity>()
.Where(Expression.Eq(Projections.Property<Entity>(x => x.SomeField), s))
.List();

String.IsNullOrEmpty in LINQ To SQL query?

My DBML exposes a record set that has a nullable nvarchar field. This nullable nvarchar field is represented as a string in my C# code.
Sometimes this field is null, sometimes it is an empty string, and sometimes it actually has a value.
Does String.IsNullOrEmpty() work in LINQ To SQL? For instance, would the following work:
var results = from result in context.Records
where String.IsNullOrEmpty(result.Info) == false
select result;
Curiously, per MSDN String.IsNullOrEmpty is supported (by virtue of it not being unsupported), yet I can only find complaints about it not being supported.
However, if it does work you should not explicitly compare it to a boolean value, instead:
var results = from result in context.Records
/*XXX broke :( where !String.IsNullOrEmpty(result.Info) */
where !(result.Info == null || result.Info.Equals(""))
select result;
I don't know if that works, but I'm sure this does:
where (result.Info ?? "") != ""
(strongly recommend the parens, query generator can get confused without them)
It is not supported since attempting to use it results in a NotSupportedException being thrown with this message:
Method 'Boolean IsNullOrEmpty(System.String)' has no supported
translation to SQL.
Instead, you can use this approach to do the same thing:
var results = from result in context.Records
where result.Info != null && result.Info.Length > 0
select result;
You may also use result.Info != String.Empty instead of checking the length. Both approaches will work.
I had problems with all answers except for #ahmad-mageed's answer.
Ended up using a more concise syntax of:
where (result.Info ?? "").Length > 0
Or
result => (result.Info ?? "").Length > 0
You can use a function as argument to the Where method if you use a Linq query, e.g.
var results = context.Records.Where(string.IsNullOrEmpty);
But in this case that would give you all null or empty elements, instead of the opposite. Then create an extension method to the string class (e.g. string.IsNotNullOrEmpty) or do something like this:
var results = context.Records.Except(context.Records.Where(string.IsNullOrEmpty));

LINQ returns 0 results if using nullable int variable, accurate results if using "null"

I have a table called "test", which only has 1 column, "NullableInt" (nullable int type)
The records are: 1, 2, null
int? nullableInt = null;
var t = db.tests.Where(x => x.NullableInt == null).ToList(); // returns 1 record
var t2 = db.tests.Where(x => x.NullableInt == nullableInt).ToList(); // returns 0 records
For some reason, t2 returns 0 records, even tho it's using "nullableInt" variable, which has a value of null, just like t, which is comparing against "null"
Any help would be greatly appreciated!
Yep - it's a bug in LINQ-to-SQL / Entity Framework. IS NULL queries will only be generated if you hardcode null into the query, instead of a variable that happens to currently be null.
The second query will generate
SELECT .......
WHERE NullableInt == #someParam
WHERE #someParam is null.
Where the first will generate the appropriate IS NULL in the WHERE clause.
If you're using LINQ-to-SQL, you can log your queries to Console.Out to see for yourself, and if you're using EF, then ToTraceString() should show you the same info (or SQL Server profiler)
tl;dr
If you use DbContext in EF6 this is fixed.
If you're using EF5 (or ObjectContext in EF6) you need to set ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior to true. To do that on DbContext use this:
((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
.
More details
The root cause of this issue is a difference in how the database compares null values and how C# compares null values. Because you write your query in C# you want to use the semantics of C#.
In EF5 we introduced ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior, which allowed you to opt in to using C# semantics instead of database semantics. The default is false (so that existing queries don't magically start returning different results when you upgrade to EF5). But you can set it to true and both your queries will return rows.
If you are using DbContext in EF5 you need to drop down to the ObjectContext to set it:
((IObjectContextAdapter)db).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
If you are using EF6, then it's already set to true on DbContext so you are good to go. We decided this causes so much confusion it was worth taking the potential impact on existing queries.
Queries could be built in this way:
var q = db.tests;
if(nullableInt.HasValue)
{
q = q.Where(x => x.NullableInt == nullableInt.Value);
}
else
{
q = q.Where(x => x.NullableInt == null);
}
var t2 = q.ToList();
There is another solution that will always work, albeit with a small caveat:
int? nullableInt = null;
var t2 = db.tests.Where(x => object.Equals(x.NullableInt, nullableInt)).ToList();
When the value is null you will get the proper IS NULL query, however when its not null you will get something like:
SELECT ...
WHERE ([t0].[NullableInt] IS NOT NULL) AND ([t0].[NullableInt] = #p0)
Obviously it has a condition extra (the source of which is kind of puzzling). That being said, SQL Server's query optimizer should detect that, since #p0 is a non-null value, the first condition is a superset and will cut the where clause.
Would doing:
var t2 = db.tests.Where(x => x.NullableInt == nullableInt ?? null).ToList();
Work?
It seems like utter madness though.

Categories