nHibernate unable to cast Boolean to String - c#

I have the following query:
var query = from item in Session.Query<FactuurItem>()
where item.EnergieType == etype
&& (item.DienstType == null || item.DienstType == DienstType.Onbekend || item.DienstType == dtype)
&& item.IsActive == true
orderby item.Naam
select item;
Which is converted to the following SQL:
select * from [FactuurItem] factuurite0_
where
factuurite0_.EnergieType=?
and (factuurite0_.DienstType is null or factuurite0_.DienstType=? or factuurite0_.DienstType=?)
and case when factuurite0_.IsActive=1 then 'true' else 'false' end=case when ?='true' then 'true' else 'false' end
order by factuurite0_.Naam asc
Which results in the Exception:
{"Unable to cast object of type 'System.Boolean' to type 'System.String'."}
Now for my question: why??
The original query looks ok to me. The SQL, however, does not. Where do the two case-statements originate from? Apparently it tries to convert the property IsActive to a string in SQL, which it fails to do.
EDIT
Ok, found the solution. Nothing wrong with mapping etc., just with how the LINQ query is translated to SQL. In particular, how this line is translated:
&& item.IsActive == true
Somehow, this gets translated into the complex CASE-statement which ultimately results in the exception message. However, the == true-part isn't really necessary. By removing it, the translator no longer gets confused and provides the proper SQL:
factuurite0_.IsActive=1
No more CASE-statement and no more exception.

Ok, found the solution. Nothing wrong with mapping etc., just with how the LINQ query is translated to SQL. In particular, how this line is translated:
&& item.IsActive == true
Somehow, this gets translated into the complex CASE-statement which ultimately results in the exception message. However, the == true-part isn't really necessary. By removing it, the translator no longer gets confused and provides the proper SQL:
factuurite0_.IsActive=1
No more CASE-statement and no more exception.

Using Log4Net at the debug level? In some version of Hibernate and Log4Net there is an incompatibility when turn on logging at the DEBUG level. All you get is this error about 'unable to execute sql cannot cast boolean to string'. Try turning up your logging level to INFO and the problem should go away.

Related

Conditional sum using linq for Or condition

When I use two conditions joined by an OR, the result is not correct for SQL Server.
How can I fix it?
This is my LINQ code and result in SQL (that reflection created for me):
query.Where(p => ((p.Code == "100000") Or p.Code.EndsWith("200")))
query.Where(p => (p.year == "2015"))}
I added this where clause at runtime, now I add another extension method and it's not working:
query.sum(p => p.value)
Exception:
An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll but was not handled in user code
Additional information: An expression of non-boolean type specified in
a context where a condition is expected, near 'AND'.
SQL translated:
SELECT SUM([e].[Value])
FROM [acc].[Data161] AS [e]
WHERE (CASE
WHEN RIGHT([e].[Code], LEN(N'201')) = N'201'
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END |
CASE
WHEN RIGHT([e].[Code], LEN(N'199')) = N'199'
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END)
AND ([e].[SetadCode] = N'161')
The correct SQL should have = 1 before the AND.
But without sum its works fine and add a = 1 to SQL command
First off, the Or is not a valid C# operator
.Where(p => ((p.Code == "100000") Or p.Code.EndsWith("200")))
If you change it to ||, the query translates correctly and executes w/o issue.
Looking at the generated SQL, I'm pretty sure you have used the bitwise or operator (|) instead, in which case I get the same error. While this could be a EF Core translator bug, you shouldn't be using it anyway - use the logical or || operator and the generated SQL will not have all that CASE WHEN expressions, but a typical simple WHERE conditions.
You can use || instead of or
query.Where(p => ((p.Code == "100000") || p.Code.EndsWith("200")))
This is too long for a comment, but does not address the linq part of the question.
I would expect the where clause to look like this:
where (code = '100000' or p.code like '%200') and (p.year = '2015')
All that bit manipulation that is generated is jarring. Note that the above code is ANSI-standard SQL as well.

Error Converting Enum to String in LINQ Query

I have the following query, which is using Entity Framework.
Analytic firstSent = (from a in Repository.Query<Analytic>()
where a.EntityType == "Proposal" &&
a.EntityId == Program.ActiveProposal.Id &&
a.Marker == AnalyticMarker.EmailProposalUrl.ToString()
orderby a.TimestampUtc
select a).FirstOrDefault();
At run time, I get the following error:
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.
a.Marker is a string column, and AnalyticMarker.EmailProposalUrl is an enum value, and I want to compare that column against the name of that enum.
I understand that the conversion from an enum to a string is not supported by SQL, but why won't it resolve the value of this string and then pass the resulting string to SQL? That should work just fine.
Try this:
var emailProposalUrl = AnalyticMarker.EmailProposalUrl.ToString();
Analytic firstSent = (from a in Repository.Query<Analytic>()
where a.EntityType == "Proposal" &&
a.EntityId == Program.ActiveProposal.Id &&
a.Marker == emailProposalUrl
orderby a.TimestampUtc
select a).FirstOrDefault();
This other answer is explaining the reason why this could work as well..
The problem arises because ToString() isn't really executed, it is
turned into a MethodGroup and then parsed and translated to SQL.
Since there is no ToString() equivalent, the expression fails.
Well, in the places where C# will normally resolve tostring for you, it normally takes an object. Here, for example though, you're asking it to compare a string to an enum. Although C# COULD call tostring(), its not correct for it to do so.
Think about if it was int you were trying to compare against, rather than an enum. You can't compare the number 1 and a string. (Well, you can, but it involves assumptions, which is the point; the language is just trying to prod you into making the logic that little more explicit)

Linq to SQL Join and Contains Operators

in the following query
var restrictions = from p in dcTrad.quop_restricted_items
where p.entry_status == 'P' && p.batch == "PRODUCTION" && p.problem != null
from q in dcTrad.model_companies
where q.co_name != null && p.brimsec == q.primary_bsec
select new { Company = q.co_name, Restriction = p.comment ?? "Restricted without comments", Portfolio = p.problem };
I need to replace
p.brimsec == q.primary_bsec
with
p.brimsec.StartsWith ( q.primary_bsec )
but I get the following error:
Only arguments that can be evaluated on the client are supported for the String.StartsWith method
How can I make this work?
Unfortunately the LINQ to SQL translator is not smart enough to translate this code, but there's a trick that achieves the same:
p.brimsec.StartsWith ( q.primary_bsec )
Translates to:
p.brimsec.SubString(0, q.primary_bsec.Length) == q.primary_bsec
The LINQ to SQL translator handles this just fine, and the semantics are equivalent to StartsWith.
Frankly, I don't see why translating StartsWith properly for server-side arguments was so hard that the LINQ developers just decided to throw an error instead.
Basically the linq to sql does not know how to convert startswith to Sql. This is because internally at run time your linq is code generated to sql.
You may go about achieving this by creating a UDF (user defined function in sql) and using it from your linq statement.
The article is as below:
http://msdn.microsoft.com/en-us/library/bb399416.aspx
Andrew
I think the problem you're running into is that linq-to-sql has no translation of String.StartsWith into SQL. String.Contains does work, though - you'd need to go through your resultant collection and filter out the items that don't start with q.primary_bsec.

Why is this if/then statement returning as true?

I have a rather ugly service job that runs through a legacy database and compares it to our production database:
if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null) {
var oldDbContractItem = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).First();
// check to see if there were changes
if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp)
{
oldDbContractItem.Update(vendorContractItem);
}
}
I will get an error on var oldDbContratItem, "Sequence contains no elements", yet I just did a != null check. This must be simple, what's going on?
If I could teach people just one thing about LINQ it's this: the value of a query expression is an object that represents the query, not the results of the query. Fundamentally that's your problem; you're treating the query as its results. A query isn't a result any more than a restaurant is a club sandwich. A restaurant is a device which produces club sandwiches; a query is a device that produces results.
This is the test against null you are doing:
vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number) != null
That will always be true; That will always return at least an empty sequence... never a null.
You might be meaning to test that its length is greater than 0?
There's an easier way, though, IMO. Call FirstOrDefault() instead of First() and leave out the pre-test completely. Then instead, test if the result of FirstOrDefault() is null.
var oldDbContractItem = vendorContract.Item
.Where(x => x.ItemNumber == contractItem.Item_Number).FirstOrDefault();
if(oldDbContractItem != null) //would be null if there are no items
{
// check to see if there were changes
if (oldDbContractItem.DateStamp != vendorContractItem.Date_Stamp)
{
oldDbContractItem.Update(vendorContractItem);
}
}
}
Because your query returned a container, it just happened to be empty, the null check is on the return not what the return contains.
try this instead...
if (vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).Any())
{
.....
}
Don't run the query twice. It's inefficient and may introduce a race condition into your code. Also, your logic is much better supported by either using IEnumerator<T> directly, or with a foreach loop.
Use either:
var result = vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number).GetEnumerator();
if (result.MoveNext) {
var oldDbContractItem = result.Current;
// ...
}
or
foreach (var oldDbContractItem in vendorContract.Item.Where(x => x.ItemNumber == contractItem.Item_Number)) {
// ...
break;
}
A sequence with no elements is still an object.
Where can return a non-null value, but still resolve to a sequence containing no elements. In your failing statement you are using something else, which is Where().First(). That will be null if the sequence from Where is indeed empty.
There is difference between null and an empty sequence (e.g. something similar to new List<T>()). What you want to do is to use Any() to check whether the sequence contains any elements, or rather replace First() with FirstOrDefault(), which returns null if the sequence contains no element. (If you use that, make sure that null is not a valid value that could be returned by First()).
After using a fix from the accepted answer, you can further debug the LINQ situation (if need be) by using the following steps. LINQ is an exciting technology but takes a while to wraps ones' head around it - a bit of a paradigm shift I would say. Eric's answer hit the nail on the head because he likely helped build the stuff!
Debugging Steps for LINQ
To deal with the issue of no results from the second statement change .First() to .FirstOrDefault() If nothing is found it will return the default value of the data type - if the data type is a class it will return a null value. Your second statement should then work with a null check too and without error.
Then you can debug your LINQ statement to find out why it's doing what it does.
if using LINQ to SQL, Intellisense in Visual Studio 2010 will show you the SQL generated when you hover over a query variable (not the result of a query). If you need the visualizer for VS 2008 it's here
Similarily if you're using LINQ to Entity Framework, you can get the generated SQL using the visualizer plugin here.
I always take the generated SQL from these tools, paste it directly into a query window and run it there. It will show you the empty set you're getting back and if that's a problem you can further debug it in this manner of visualizing the generated statements.

Conditional shortcuts in LinqToSql query

Here's a little LinqToSql GOTCHA:
// Returns the number of counties in a state,
// or all counties in the USA if the state is null
public static int CountCounties(State s) {
var q =
from cy in County.GetTable() // my method to get the ITable
where (s == null || s.Code == cy.StateCode) // shortcut OR operator, right...?
select cy;
return q.Count();
}
Guess what - if you pass a null State object to this method, you get a null reference exception! It seems that LinqToSql doesn't use the || shortcut operator as a shortcut!
Answer credit goes to whoever proposes the best explanation & workaround for this.
If it's linq to sql then remeber that Linq is just parsing your query into SQL.
It is therefore sending both of your where clauses to the database, hence the exception. I dont find this surprising really, though it is arguably wrong.
You will just have to do an independant check.
if (!string.isNullOrEmpty(state.statecode)
q = q.where( s => s.code == state.statecode
This is not related to LINQ in general. In this case, the LINQ-to-SQL provider tries to parse the your lambda expression and make it a TSQL query. It cannot make too many assumptions based on your expression since it's trying to delegate most of the work to the database.
Long story short, the provider simply cannot translate it to SQL.

Categories