LINQ to Entities does not recognize the method Int32 CompareString - vb.net

I'm trying to execute the query below. However I'm getting the exception message:
LINQ to Entities does not recognize the method 'Int32 CompareString(System.String, System.String, Boolean)' method, and this method cannot be translated into a store expression.
I've narrowed it down to specifically the line "Where hearing.JudgeName = judge" but I see no reason why this would not be translated. JudgeName and judge are both strings and I'm connecting to a sql server. What is going on here?
Dim query = From hearing In Context.Hearings
From appeal In hearing.Appeals
From participation In appeal.Participations
Where Not appeal.SentDate.HasValue
Where appeal.StartDate <= pendingAsOfDate
Where hearing.JudgeName = judge
Even simplifying the query produces the same exception:
Dim query = From hearing In Context.Hearings
Where hearing.JudgeName = "Test"
Me.HasKey(Function(x) x.HearingId)
Me.Property(Function(x) x.HearingId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
Me.Property(Function(x) x.JudgeName).HasMaxLength(50)

Related

ERROR [37000] [IBM][CLI Driver] CLI0118E Invalid SQL syntax. SQLSTATE=37000

I have a simple SQL statement query that is executed as command from C# code. It is targetting DB2. I created variables for the server/schemas as follows. It throws error.
private const string DB2Query
= #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
I get this error.
ERROR [37000] [IBM][CLI Driver] CLI0118E Invalid SQL syntax. SQLSTATE=37000
However, I don't get that error when executing from SQL as follows:
SELECT Name as Name
FROM MyServer..FOR3.Application
WHERE ID = 'MOM'
To support this, I tried to also do something like below in code, still throws different error.
private const string DB2Query
= #"SELECT Name as Name FROM {ServerName}..{Schema}.Application WHERE ID = ?";
It throws error on this line of code:
DataApplicationBlockHelper<string>.Get(db, dbCommand, Obj);
UPDATE
I found the culprit. It's not replacing the {Schema} placeholder. When I actually removed that from query and placed the schema name, it worked like a charm. It's a .net thing I believe? Can someone please help how to replace {Schema} with a value fetched from web.config?
While I can't really speak to the syntax of DB2 queries themselves, so I'll rely on your assertion that the query itself should work...
What you have in C# is simply a string and nothing more:
private const string DB2Query = #"SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
Note that there's no need for the # operator in this string definition, so let's simplify:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
While this string appears intuitively to have a placeholder that can be replaced with a value, if there's no code which does that anywhere then it won't happen. For that you have a few options. For example, you can use a placeholder that string.Format() understands:
private const string DB2Query = "SELECT Name as Name FROM {0}.Application WHERE ID = ?";
And then later in a method somewhere, when you want to use that string, apply the format value to it:
var sql = string.Format(DB2Query, someVariable);
In this case someVariable (which doesn't even need to be a variable and could be a string literal) would be used to replace the placeholder in the string.
Or, if you want to keep the named placeholder, you can potentially replace it manually:
private const string DB2Query = "SELECT Name as Name FROM {Schema}.Application WHERE ID = ?";
and later in a method:
var sql = DB2Query.Replace("{Schema}", someVariable);
This would observably accomplish the same thing, perhaps with an extremely minor performance difference.
You could also take advantage of both approaches by using the more recent language feature of string interpolation. This would use the $ operator to apply format placeholders in place directly. I don't think you can use this in a const, it's more for a local variable. Something like this:
var sql = $"SELECT Name as Name FROM {someVariable}.Application WHERE ID = ?";
This would still perform the same replacement, putting someVariable where the placeholder is, it's just using a more concise syntax than a call to string.Format(). One thing to note about this syntax is that it makes it look more like this interpolation is happening directly in-place on the string. It's still a multi-step process behind the scenes, which is why it likely won't work on a const or on class members at all (and should I imagine produce a compiler error).
Remember that strings are immutable, so any operation you perform which modifies a string would be returning a new string rather than modifying the existing one in place.
In any case, you'll of course also need to apply your query parameter for the ? placeholder. Note that what C# considers to be a placeholder in a string formatting/interpolating operation and what DB2 considers to be a placeholder for a query parameter are two entirely different things which happen at different times in different environments. (One in the .NET runtime, one in the database server's query execution.) But again, I'm relying on your assertion that the database query itself works and the only problem we're focusing on here is the C# string placeholder.

"LINQ to Entities does not recognize the method" without using local

I want to query count of selected products with a Combobox in VB.NET, Linq and Entity Framework 6. This query generates error (cmbProducts is a Combobox):
Dim Count = (From Product In db.Products
Where Product.Type = cmbProducts.SelectedValue
).Count
And this is the error:
LINQ to Entities does not recognize the method 'System.Object CompareObjectEqual(System.Object, System.Object, Boolean)' method, and this method cannot be translated into a store expression.
But when I run this query with db.Products.local, it executes without any errors:
Dim Count = (From Product In db.Products.local
Where Product.Type = cmbProducts.SelectedValue
).Count
You really should turn Option Strict On in the project properties and also in the IDE options, so it will be On by default for all future projects. If you do that then that code won't even compile. That would force you to do as you should have and cast the SelectedValue, which is type Object, as the actual type of the underlying object, which is presumably String or Integer. You can use DirectCast or else CInt, CStr or the like to perform the cast, e.g.
Where Product.Type = CInt(cmbProducts.SelectedValue)
Ideally, you should be assigning the result of that cast to a variable and using that in your LINQ query. It's important to remember that, while LINQ syntax is always the same, each LINQ provider has a different implementation. LINQ to Entities converts your query to SQL that it can execute against the database and that means that some things that are supported by LINQ in general, and will thus compile, will actually fail at run-time against LINQ to Entities. It's generally a good idea to keep anything remotely exotic out of your EF queries. You'd probably be OK in this case but it's a good habit to get into as it can help avoid subtle issues.
Make sure they are of the same type.
I think Product.Type is a string but cmbProducts.SelectedValue is an int, try to use cmbProducts.SelectedItem.Text
When comparing locally .Net will be able to compare them by SQL Server may not.

Why do I get Stackoverflow Exception when calling ToList (after orderBy) on IEnumerable?

I have a piece of LINQ code that results in Stack Overflow Exception and I can't figure why. I will keep the code as is since I can't tell which part is the cause.
Consider we have a list of booking records:
Class BookRecords
Public START_DATE As String
Public END_DATE As String
Public BOOK_NUM As String
End Class
And the task is to find out all records which (1)Start time is within 15 minutes from now & (2)Is consecutive to records in (1).
Here's how I do it:
Private Function ValidRec(RecordList As List(Of BookRecords)) As List(Of BookRecords)
Dim timeNow = Date.Now
'Valid records by itself
Dim validRecords = RecordList.Where(
Function(r)
Dim startDate As DateTime
'VVVVV stack overflow thrown at this return
Return DateTime.TryParse(r.START_DATE, startDate) AndAlso
((startDate - timeNow).TotalMinutes < 15)
End Function)
Do
'consecutive records
Dim conseRecords = RecordList.Except(validRecords).Where(
Function(r) validRecords.Any(
Function(vr) vr.END_DATE.Equals(r.START_DATE)))
If Not conseRecords.Any() Then Exit Do
validRecords = validRecords.Concat(conseRecords.Except(validRecords))
Loop
validRecords = validRecords.OrderBy(Function(vr) vr.START_DATE) _
.ThenBy(Function(vr) vr.BOOK_NUM)
'stack overflow after the ToList line
Return validRecords.ToList
End Function
The code works fine until after the last line validRecords.ToList. And then stack overflow occurs at the Return DateTime.TryParse ... statement. Size of RecordList and validRecords are small (both 2 in testing) and there're no other threads modifying these Lists/Objects.
Why is this using up the stack? I know the LINQ statements are probably badly structured and creating lots of lists(IEnumerables?) unnecessarily but is that the cause? (that would be genuine stack overflow then, cool)
I figured making validRecords a list instead of IEnumerable would avoid the problem but I'd appreciate if someone could point out the real cause.
Apparently, you have a wrong understanding of what these LINQ methods do. Whenever you call .Where(), .Except() etc, a new enumerator is created. This enumerator only saves its characteristics (e.g. the Where enumerator saves its predicate). The query is not executed yet. It is only executed when the data is needed, e.g. when you call ToList() on the enumerator. That's why this is the place the exception is thrown.
In your concrete example, the problem is caused by the OrderBy function. Seemingly, it creates an infinite enumeration. However, I could not figure out why. Must be some nasty implementation details.
Anyway. Forcing LINQ into this problem is the wrong way. It is way more efficient and easier to implement it the classical way. This lets you explicitly specify how acceleration data structures are used (e.g. HashSets or Dictionaries) and when and how often the data is sorted. Due to its local scope, all LINQ enumerator have to re-create this structures if they need them.

LINQ, Visual Basic, & Reflection: capitalization of field names from queries returning anonymous type

Edited to answer my own question. This appears to be a LINQ/VB bug.
A simple LINQ query returning an anomymous type will sometimes change the field names specified in the query so as to capitalize them. Perhaps passing the result of the query as a parameter to a method call:
someThing.someMethod(From someStuff In stuffList _
Select text = someStuff.Name(), _
value = someStuff.Id
)
where someMethod has signature
Public Sub someMethod(ByVal list As IEnumerable(Of Object))
If you step into the execution of someMethod, and then examine the value of list in quickwatch, you may or see the field names as "text"&"value" or "Text"&"Value".
LINQ queries should not be changing the field names as specified in the query, so the correct behavior is fieldnames "text"&"value". Yet production builds of our application have the incorrect capitalization behavior (which can be determined indirectly), and debug builds have shown it both happening both ways at different times and/or for different developers' machines.
I've looked high & low for some feature of LINQ which controls this behavior, but now am virtually certain it is a bug. (msdn forum thread, MS Connect bug page)
This is likely to only cause a problem if you are using reflection, such as type.getfield() such as in
listItem = list.ElementAt(index)
itemTextField = listItem.GetType().GetField("text")
itemText = CType(itemTextField.GetValue(listItem),String)
If this happens to you, the workaround is to use overload of GetField with bindingflags to make it case-insensitive:
itemTextField = listItem.GetType().GetField("text", BindingFlags.IgnoreCase)
It must be pretty rare to encounter this bug, but maybe the next person will spend less time scratching their head if they find this info here.
=========original post===========
Getting different behavior in my debug build environment than in my coworkers' and our production envirnonment, relating to LINQ and reflection...
While running debug build of legacy code, the following code
Dim objectType As Type = obj.GetType()
Dim field As FieldInfo = objectType.GetField(name)
Dim prop As PropertyInfo = objectType.GetProperty(name)
results in Nothing for field & prop.
The value of obj is passed down from above and is the result of a LINQ query (it is a single element of the list generated by the query):
From bpt In CustomBProcessTypes Select text = bpt.Name(), value = bpt.Id
The value of name is also passed from above and is "Text" (note capitalization).
I can examine obj in the debugger and confirm that the fieldnames of the object created by the LINQ query are 'text' and 'value' (note lack of capitalization) which is what I would expect.
So failure to find the field by the capitalized name makes sense. However, our production builds and my coworkers builds do not have this problem.
Because calls to type.getfield(string) are expressly cas-sensitive, the only thing I can think of at this point is there must be some configuration of LINQ relating to auto-capitalization of column/fieldnames, and my environment is not set up the same as the others.
Using visual studio 2012. I don't know much of anything about LINQ, per se.
Anyone have any idea what could be going on here?
(NOTE: if I can get an opportunity, I'll have a coworker step through the relevant code and see if in their environment the object created by the linq query ends up with capitalized field names)
EDIT: I verified with a coworker in his debug build: his LINQ query creates a list of objects with field names "Text" and "Value", but on in my environment the LINQ query ends up with field names "text" and "value". The code is the same, but there must be something about how LINQ is configured in my environment which fails to auto-capitalize those field names, but which happens on their machines and in our production environment.
I suppose it is possible that some compiler settings are resulting in different capitalization. Normally this would make no difference because VB.NET is a case-insensitive language so obj.Text and obj.text both work just as well. But to use case insensitivity in reflection lookups, you need to specify it by including BindingFlags.IgnoreCase in the second parameter of GetField or GetProperty:
Dim field As FieldInfo = objectType.GetField(name,
BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.IgnoreCase)
I'm confused as to where name is coming from, though. Some other code is getting the field name from reflection on the query? I didn't see where this was explained in your question.
I have answered my own question (insofar as is possible). Boils down to a bug in LINQ/vb.net.
Fully explained at top of original post (edited in). Hope this saves someone time in the future.

Argument Exception in DeriveParameters method

Dim id as integer = 1
Dim command as sqlcommand
Dim reader as idatareader
command = db.GetSqlStringCommand("select id, image, caption from profile where id = #id and image IS NOT NULL Order By NEWID()")
db.AddInParameter(command, "#id", DbType.Int32, id)
reader = db.ExecuteReader(Command)
The code is throwing an error I've never seen before....
SqlCommand.DeriveParameters failed because the SqlCommand.CommandText property value is an invalid multipart name "/port:4544 /path:"C:\sitepath" /vpath:"/sitepath"", incorrect usage of quotes.
How do I fix that error.
It looks like the code is failing at a deeper level than the code you have posted. I have received a similar error from within SQLServer Management Studio when I have not correctly defined the path to my DB.
How are you setting up the object 'db'?
What is your connection string? (Without password! :-))
The error is obviously related the badly formed string in terms of the quote positions. Do you know where entlib is constructing this string and how parts of it such as "C:\sitepath" are appearing with quotes as opposed to being appended as string literals?
I wonder if somewhere there is a declaration such as
Dim sRootPath As String = """C:\sitepath"""
..which is leading to the quotes being inserted into the constructed string.
I never saw this error, but perhaps this link helps: http://entlib.codeplex.com/Thread/View.aspx?ThreadId=60513
"What version of entlib is this? There's no overload in 4.1 for ExecuteReader that accepts an sql statement, there is one accepting a string but it should be the stored procedure name. Probably you are using this overload but I'm not sure since I'm getting a different error if I pass an sql statement"
I don't know about the error but the order by NEWID() bothers me a little. With newid() SQLServer calls the newid() function for each row in your dataset.