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

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.

Related

"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.

VB.Net Linq to Entities Null Comparison - 'Is Nothing' or '= Nothing'?

We have several projects in VB.Net, using .Net Framework 4 and Linq to Entities for many of our SQL queries. Moving to EF is a new shift for us (been using it for about 4-6 months) and has the backing of upper management because we can code so much faster. We still use a lot of stored procs, but we even execute those through Linq to Entities as well.
I'm hoping to clear some confusion up and I can't find a direct answer that makes sense. We have some queries where we want records where a specific field has a NULL value. These are simple select queries, no aggregates or left joins, etc. Microsoft recommends the query look something like this MSDN Link:
dim query = from a in MyContext.MyTables
Where a.MyField = Nothing
Select a
I have several projects where I do exactly this and it works great, no warnings in the IDE. Recently a new project was created by another developer and when he did his null check like above, we all get this warning in the IDE:
Warning 1 This expression will always evaluate to Nothing (due to null propagation from the equals operator). To check if the value is null consider using 'Is Nothing'.
Comparing the projects, option explicit and option strict are on for each one. If we ignore the warning, we get the exact record set we are looking for when the app runs. The warning goes away if I change the = sign to IS. But why did this warning appear in one project and not others? It's confusing when even on MSDN there are examples using the equals operator.
Generated column should be a Nullable(Of T)
So you can check if that field has value or not like this:
dim query = from a in MyContext.MyTables
Where Not a.MyField.HasValue
Select a
I believe what you're seeing here is that MyField is a Nullable(Of T) type. Likely a primitive Integer, Single, etc ...
The reason you're seeing this warning is because the compiler promotes the normal equality operator for the primitive type to the Nullable(Of T) version. It essentially executes the following
Dim myField As Integer? = a.MyField
Dim other As Integer? = Nothing
If myField = other Then
...
End If
The issue though is that when Integer? has the value Nothing it won't compare equal to anything. Hence the above Where clause will always return False. The compiler is attempting to warn you about this problematic corner of Nullable(Of T) and push you to a Is Nothing check which will determine if a.MyField has a non-null value.
This blog article has a very detailed explanation of why this warning is being generated and all of the mechanics behind it. The article is written for C# but the basic premise is applicable to VB.Net as well.
http://blogs.msdn.com/b/abhinaba/archive/2005/12/14/503533.aspx
At least in LINQ to objects you can use this instead:
Nullable(Of Integer).Equals(a, b)
This works fine with both, either or none of the two values being Nothing.

LINQ to Entities does not recognize the method [Type] GetValue[Type]

I've a simple class like this:
Public Class CalculationParameter{
public Long TariffId{get;set;}
}
In a workflow activity, I've an Assign like this:
(From tariffDetail In db.Context.TariffDetails
Where tariffDetial.TariffId = calculationParameter.TariffId).FirstOrDefault()
Dto is passed to Activity as an Input Argument.
It raise following error and I'm wondering how to assign Id. Any Idea?
LINQ to Entities does not recognize the method 'Int64
GetValue[Int64](System.Activities.LocationReference)' method, and this
method cannot be translated into a store expression.
How can I assign the calculationParameter.TariffId to tariffDetial.TariffId?!
UPDATE:
Screen shot attached shows that how I'm trying to assign calculationParameter.TariffId to tariffDetail.TariffId (car.Id = Dto.Id) and the query result should assign to CurrentTrafficDetail object.
Here's your problem. I don't know if there is a solution to it.
As you said in a (now deleted, unfortunately necessitating that I answer) comment, the exception you're getting is
LINQ to Entities does not recognize the method Int64 GetValue[Int64](System.Activities.LocationReference) method, and this method cannot be translated into a store expression.
in your Linq query, calculationParameter is a Variable defined on the workflow. That Variable is actually an instance that extends the type System.Activities.LocationReference and NOT CalculationParameter.
Normally, when the workflow executes, the LocationReference holds all the information it needs to find the value which is assigned to it. That value isn't retrieved until the last possible moment. At runtime, the process of retrieval (getting the executing context, getting the value, converting it to the expected type) is managed by the workflow.
However, when you introduce Linq into the mix, we have the issue you are experiencing. As you may or may not know, your expression gets compiled into the extension method version of the same.
(From tariffDetail In db.Context.TariffDetails
Where tariffDetial.TariffId = calculationParameter.TariffId)
.FirstOrDefault()
is compiled to
db.Context.TariffDetails
.Where(x => x.TariffId = calculationParameter.TariffId)
.FirstOrDefault();
When this executes, L2E doesn't actually execute this code. It gets interpreted and converted into a SQL query which is executed against the database.
As the interpreter isn't omniscient, there are a well defined set of limitations on what methods you can use in a L2S query.
Unfortunately for you, getting the current value of a LocationReference is not one of them.
TL:DR You cannot do this.
As for workarounds, the only thing I think you can do is create a series of extension methods on your data context type or add methods to your CalculationParameter class that you can call from within the Expression Editor. You can create your Linq to Entities queries within these methods, as all types will already have been dereferenced by the workflow runtime, which means you won't have to worry about the L2E interpreter choking on LocationReferences.
*Edit: A workaround can be found here (thanks to Slauma who mentioned this in a comment on the question)

Exclamation Marks in a Query SQL

I'm reading over this query, and I came upon a line where I don't understand heres the line
[FETT List]![FETT Search]
FETT List is a table
FETT Search is a column in FETT List
Can someone explain what the exclamation mark means?
Thanks
Well, you learn something new every day!
I had originally planned to explain that if you'd said the reference was [Forms]![FETT List]![FETT Search], then it would be easy to explain, as a reference to the [FETT Search] control on the [FETT List] form. But without a parent collection (either Reports of Forms), it doesn't look like a valid reference in any context within a SQL statement.
But then I thought to test it, and discovered (to my surprise) that this SQL statement is treated as valid in an Access form:
SELECT [tblCustomer]![LastName] AS LastName
FROM tblCustomer;
In Access, that is 100% equivalent to this SQL statement:
SELECT tblCustomer.LastName
FROM tblCustomer;
…so I don't understand why anyone would write it, except if they forgot the context (or never understood it in the first place). It could be a case of aliasing gone wrong, but it's not what I consider good form.
Now, the long answer to the general question of ! (bang) vs. . (dot):
In general, in Access, the bang operator delineates the default collection of an object and its items. The dot operator delineates an object and its methods, properties and members.
That is for Access, and applies to Access objects and the object model for Access.
But you also use SQL in Access, and so you also have TableName.FieldName in SQL, where the dot operator separates an item in a default collection. TableName.FieldName could be considered to be short for TableName.Fields("FieldName"), as you find with Forms!MyForm!MyControl being equivalent to Forms!MyForm.Controls("MyControl"). But this rule doesn't apply in SQL -- TableName.Fields("FieldName") is not valid SQL, only TableName.FieldName is.
So, you have to keep straight which paradigm is controlling the namespace you're working in, i.e., whether it's an Access namespace or a SQL namespace.
Forms!MyForm is also equivalent to Forms.Item("MyForm"), so the ultra-long form would be Forms.Items("MyForm").Controls("MyControl"). Note how the bang operator is a shortcut for the longer form version with the dot operator, so the bang operator is quite frequently used in preference to the dot operator. Note also that the longer form ends up being used when you need to refer to an item whose name is stored in a variable, which is not possible with the bang operator:
Dim strForm As String
strForm = "MyForm"
' This is OK
Debug.Print Forms(strForm).Controls.Count
' This is not
Debug.Print Forms!strForm.Controls.Count
Also, in VBA code, Microsoft has engineered things to obfuscate this distinction in Forms and Reports, where it used to be that Me!MyFavoriteControl was legal as a control reference, and Me.MyFavoriteControl would have been legal only as a reference to a custom property (or module-level variable, which would be member of the object). You could also unwisely name a function or sub "MyFavoriteControl" and it could be referred to with the dot operator.
But with the introduction of VBA, MS introduced implicitly-created (and maintained) hidden property wrappers around all controls so that you could use the dot operator. This had one huge advantage, and that is compile-time checking of control references. That is, if you type Me.MyFavoriteControl and there is no control by that name and no other member of any kind with that name within the form/report's namespace, then you would get a compile-time error (indeed, you'd be informed of the error as soon as you left the line of code where you made the error). So, if you had this code:
Debug.Print Me.Control1
... and you renamed Control1 to be MyControl, you'd get an error the next time you compiled the code.
What could be the downside of compile-time checking? Well, several things:
code becomes harder for the programmer to understand on sight. In the past, Me!Reference meant an item in the default collection of a form/report (which is a union of the Fields and Controls collections). But Me.Reference could be a control or a field or a custom property or a public module-level variable or a public sub/function or, or, or... So, it sacrifices immediate code comprehensibility.
you are depending on implicit behavior of VBA and its compilation. While this is usually an OK thing to do (particularly if you take good care of your code), VBA compilation is very complex and subject to corruption. Over the years, experienced developers have reported that using the dot operator makes code more subject to corruption, since it adds another layer of hidden code that can get out of synch with the parts of the the application that you can alter explicitly.
since you can't control those implicit property wrappers, when they go wrong, you have to recreate your module-bearing object from scratch (usually SaveAsText is sufficient to clear the corruption without losing anything).
So, many experienced developers (myself included) do not use the dot operator for controls on forms/reports.
It's not such a big sacrifice as some may think if you use a standard set of naming conventions. For instance, with bound controls on forms, a let them use the default names (i.e., the name of the field the control is bound to). If I don't refer to the control in code, I never change its name. But the first time I refer to it in code, I change its name so that the control name is distinct from the name of the field it is bound to (this disambiguation is crucial in certain contexts). So, a textbox called MyField becomes txtMyField at the time I decide to refer to it in code. The only time I'd ever change the field name after code is written is if I somehow decided that the field was misnamed. In that case, it's easy enough to do a Find/Replace.
Some argue that they can't give up the Intellisense, but it's not true that you entirely give it up when you use the bang operator. Yes, you give up the "really intelligent" Intellisense, i.e., the version that limits the Intellisense list to the methods/properties/members of the selected object, but I don't need it for that -- I need Intellisense to save keystrokes, and with Ctrl+SPACEBAR you get a full Intellisense list that autocompletes just like the context-specific Intellisense, and can then short-circuit the typing.
Another area of dot/bang confusion is with DAO recordsets in VBA code, in which you use the dot operator for the SQL that you use to open your recordset and the bang operator to refer to fields in the resulting recordset:
Dim rs As DAO.Recordset
Set rs = CurrentDB.OpenRecordset("SELECT MyTable.MyField FROM MyTable;")
rs.MoveFirst
Debug.Print rs!MyField
rs.Close
Set rs = Nothing
If you keep in mind which namespace you're working in, this is not so confusing -- the dot is used in the SQL statement and the bang in the DAO code.
So, to summarize:
in SQL, you use the dot operator for fields in tables.
in forms and reports, you use the bang operator for controls and the dot operator for properties/methods (though you can also use the dot operator, but it's not necessarily advisable).
in VBA code, references to controls on forms and reports may use either dot or bang, though the dot may be prone to possible code corruption.
in SQL, you may see the bang operator used, but only if there is a reference to a control on an Access form or report, of the form "Form!FormName!ControlName" or "Report!ReportName!ControlName".
in VBA code working with DAO recordsets, you may see both the dot and bang operator, the former in defining the SQL that is used to open the recordset, and the latter to refer to fields in the resulting recordset once it is open.
Is that complicated enough for you?
Generally you see this in MS Access code (for the exclamation mark, a period for SQL server). You can refer to a column by table.column or if you give the table an alias, then by alias.column. You might do this if you want to be specific when using joins, or you may have to do it when two (or more) tables in a query/join have the same column name in each table.
I think that the esclamation mark is only a conventional separator.
In Oracle PL/SQL you use dot:
[FETT List].[FETT Search]
Any other clues?!

Linq Update Problem

I'm having some problems updating the database using Linq...
Public Shared Function Save(ByRef appointment As MyLinq.Appointment, ByRef db As MyEntities) As Boolean
If appointment.id = 0 Then
db.AddToAppointments(appointment)
Else
db.AttachTo("Appointments", appointment)
'db.ApplyPropertyChanges("Appointments", appointment)
End If
Return db.SaveChanges() > 0
End Function
So Insert works fine, i have tryed both lines of code for the update with no sucesss... The first one goes ok but no update is performed, the second one throws an exception...
Can someone point out what i am missing?
EDIT:
Sorry for the late reply... I had some internet connection problems...
I had to "make it work", so now my update code is fecthing the record from the database, updating and then executing "SaveChanges" method. It works but I am not happy having to query the database to perform an Update... If you have any idea how I could do this without an update I would appreciate :)
Chris: It was a nice try, but my refresh method only allows me to choose "RefreshMode.ClientWins" or "RefreshMode.StoreWins" I tried with ClientWins with no success...
Razzie: I am sorry but i did not save the exception and it no longer occurs... It was saying that my record did not have a key associated (or something similar)
Jon Skeet: In Vb.Net we have to specify if the parameter goes ByVal or ByRef, we can't omit like in C#
The code you have doesn't look exactly like what I'm used to (linq to sql), but it does look a little similar; Is this Entity Framework?
I know with Linq to SQL, simply attaching an object to the data context isn't enough, you also have to make sure that the data context knows what the original values are so it knows which columns to update. In Linq to SQL that can be achieved like this:
db.Refresh(RefreshMode.KeepCurrentValues, appointment)
Maybe look around and see if you can achieve something similar in whatever framework you are using.
The ApplyPropertyChanges() call is important otherwise the item you are attaching is assumed to be in an unchanged state. However... for ApplyPropertyChanges to work properly the original object must exist in the ObjectContext which means either querying for it again (which I think you are now doing) or using the same object context that you originally pulled the item from.
Some more info here - http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.applypropertychanges.aspx