String.IsNullOrEmpty in LINQ To SQL query? - c#

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

Related

How to check if IQueryable element/ item contains a property in C#?

I have an IQueryable as follows
var issues = from i in jira.Issues.Queryable
where i.Project == project.Key
orderby i.Created
select i;
I am trying to get the count where issue type is Bug like this:
TotalIssues = issues.ToList().Where(i => i.Type.Name == "Bug").Count();
The problem here is there are some issues without a type. How can I check if the issue type exists?
You can try null propagation ?. in order to have null for Name if any item or item.Type are null:
TotalIssues = issues
.AsEnumerable() // Linq to objects from now on; no need in materialization
.Count(item => "Bug".Equals(item?.Type?.Name)); // just count
You don't want issues without type. You can keep code simple by not taking issues without type into account.
var bugsCount = issues.AsEnumerable()
.Where(issue => issue.Type != null)
.Count(issue => issue.Type.Name == "Bug");
You can chain as much .Where clauses as you wish, items will be still enumerated only once.
What do you mean? Some issues don't have Type Property? Or Some issues's property Type is null?
If it is some issues don't have Type Property, I think you can specify issues's class or interface. Or you can use reflection.
If it is some issues's property Type is null, you can check Type is not null first.
From your comment, I think i may be null too.
i!=null && i.Type!=null && i.Type.Name=="Bug"

LINQ to Entities does not recognize the method IsNullOrWhiteSpace

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

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.

Are ternary operators not valid for linq-to-sql queries?

I am trying to display a nullable date time in my JSON response. In my MVC Controller I am running the following query:
var requests =
(from r in _context.TestRequests
where r.scheduled_time == null && r.TestRequestRuns.Count > 0
select new
{
id = r.id,
name = r.name,
start = DateAndTimeDisplayString(r.TestRequestRuns.First().start_dt),
end = r.TestRequestRuns.First().end_dt.HasValue
? DateAndTimeDisplayString(r.TestRequestRuns.First().end_dt.Value)
: string.Empty
});
When I run requests.ToArray() I get the following exception:
Could not translate expression '
Table(TestRequest)
.Where(r =>
((r.scheduled_time == null) AndAlso (r.TestRequestRuns.Count > 0)))
.Select(r => new <>f__AnonymousType18`4(id = r.id, name = r.name,
start = value(QAWebTools.Controllers.TestRequestsController).
DateAndTimeDisplayString(r.TestRequestRuns.First().start_dt),
end = IIF(r.TestRequestRuns.First().end_dt.HasValue,
value(QAWebTools.Controllers.TestRequestsController).
DateAndTimeDisplayString(r.TestRequestRuns.First().end_dt.Value),
Invoke(value(System.Func`1[System.String])))))'
into SQL and could not treat it as a local expression.
If I comment out the end = line, everything seems to run correctly, so it doesn't seem to be the use of my local DateAndTimeDisplayString method, so the only thing I can think of is Linq to Sql doesn't like Ternary operators? I think I've used ternary operators before, but I can't remember if I did it in this code base or another code base (that uses EF4 instead of L2S).
Is this true, or am I missing some other issue?
Modify the DateAndTimeDisplayString to accept a different argument type and have a query like this:
end = DateAndTimeDisplayString(r.TestRequestRuns.FirstOrDefault())
This way, you can do the ternary stuff in your code.
By the way, it's actually this part which looks bad, because the ternary gets translated to IIF and seems to be handled, maybe try a null string:
Invoke(value(System.Func`1[System.String])))))
Have you considered using the Null coalescing operationg? (??)
Your code would then look like this:
end = r.TestRequestRuns.First().end_dt ?? string.Empty
You could modify your DateAndTimeDisplayString method to return null if passed null.

Linq returns list or single object

I have a Linq to Entities query like this one:
var results = from r in entities.MachineRevision
where r.Machine.IdMachine == pIdMachine
&& r.Category == (int)pCategory
select r;
Usually, I use the code below to check if some results are returned:
if (results.Count() > 0)
{
return new oMachineRevision(results.First().IdMachineRevision);
}
However, I'm getting NotSupportedException in the if condition.
The error message is: Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Note that pCategory is an Enum type.
EDIT: Based on your update, the error may be related to an enum in your entity class. See this blog entry for more information and a work-around. I'm leaving my original answer as an improvement on your query syntax.
Try doing the selection of the first entity in the query itself using FirstOrDefault and then check if the result is null.
int compareCategory = (int)pCategory; // just a guess
var result = (from r in entities.MachineRevision
where r.Machine.IdMachine == pIdMachine
&& r.Category == compareCategory
select r).FirstOrDefault();
if (result != null)
{
return new oMachineRevision(result.IdMachineRevision);
}
Why not just use FirstOrDefault() instead, and check for null? I can't see the benefit in querying for the count and then taking the first element.
In the standard implementation of linq, the operators "select" and "where" map to methods that return an IEnumerable or IQueryable. So standard linq methods when used should always return an IEnumerable from your query not a single object.
But linq methods that are candidates for the linq operators are not restricted to methods returning IEnumerables, any method returning anything can be chosen.
In case you have instance methods named "Select" and "Where" that return a single object or extensions methods that are specific to your class and return a single object those will be used instead of the standard linq ones.
My guess is that either a "Select" or "Where" method defined in your class is making linq return a single value instead of a IEnumerable<T>.
I didn't know different anonymous objects would be created depending on the query result. I guess they just wanted results to be of type IEnumerable
How about using a foreach?
var results = from r in entities.MachineRevision
where r.Machine.IdMachine == pIdMachine
&& r.Category == pCategory
select r;
foreach( var r in results )
{
yield return new oMachineRevision( r.IdMachineRevision );
}
This goes for all implicit types too. I must admit I keep forgetting this and that's how I came across this post.
if you have
class ClassA {
...
private string value;
...
public static implicit operator string(ClassA value)
{
return value.ToString();
}
...
}
you need to explictly cast the class to astring for comparison.
so I usually do this
var myClassAStr = myClassA.ToString();
var x = (from a in entites where a.ValToCompare == myClassAStr select a).first();
// do stuff with x
...
try using
IENumerable<MachineRevision> results = from r in entities.MachineRevision
...
instead.
I think its the var that's causing your issue.
Edit:
Read the error message. "Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."
One of these comparisions is with a type that is not int, string or guid. I'm guessing the Category.
r.Machine.IdMachine == pIdMachine && r.Category == pCategory
Interestingly, LinqToSql will allow this construction. Don't know why LinqToEntities does not support this.
I think you could also select the item you want another, simpler way by using lambda expressions.
var result = entities.MachineRevision
.Where(x => x.Machine.IdMachine == pIdMachine)
.Where(y => y.Category == (int)pCategory)
.FirstOrDefault();
if (result != null)
{
return new oMachineRevision(result.IdMachineRevision);
}
and then proceeding as you would normally

Categories