LINQ to Entities does not recognize the method [Type] GetValue[Type] - vb.net

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)

Related

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.

VBA: how to test for object equality (whether two variables reference the same object)

What is the operator or function to test whether two variables of the same custom object type refer to the same object? I've tried
If myObject = yourObject Then
But get a runtime error 438 object doesn't support this property or method. I'm guessing that's telling me to override the '=' operator to test if all the fields of the two objects have the same value. But what I want is to test whether they are the same object.
I'm guessing that's telling me to override the '=' operator to test if all the fields of the two objects have the same value.
No, it tells you the objects don't have a default property which would have been called otherwise, and the returned results compared.
You test reference equality with Is
If myObject Is yourObject Then
You need to serialize the objects somehow and then compare attribute by attribute values. The "is" operator is as dumb as it gets, it only matches if another object is the same instance assigned to the compared variable.
I suggest using a jsonStringify library. I adapted one for my DexTools.xlam open source project https://github.com/dexterial/Dextools/tree/master/Main starting from the Parsing JSON in Excel VBA post. It has much more added features since I added quite a few other excel objects serialization/hashing options and it is made using the vba test driven development that DexTools incorporates. It is still work in progress so dont expect miracles

Why does resolveBinding() return null even though I setResolveBindings(true) on my ASTParser?

I am writing an Eclipse plug-in that uses JDT AST's ASTParser to parse a method. I am looking within that method for the creation of a particular type of object.
When I find a ClassInstanceCreation, I call getType() on it to see what type is being instantiated. I want to be sure that the fully-resolved type being dealt with there is the one I think it is, so I tell the resultant Type object to resolveBinding(). I get null back even though there are no compilation errors and even though I called setResolveBindings(true) on my ASTParser. I gave my ASTParser (via setSource()) the ICompilationUnit that contains my method, so the parser has access to the entire workspace context.
final IMethod method = ...;
final ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setSource(method.getCompilationUnit());
parser.setSourceRange(method.getSourceRange().getOffset(), method.getSourceRange().getLength());
parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
final TypeDeclaration astRoot = (TypeDeclaration) parser.createAST(null);
final ClassInstanceCreation classInstanceCreation = walkAstAndFindMyExpression(astRoot);
final Type instantiatedType = classInstanceCreation.getType();
System.out.println("BINDING: " + instantiatedType.resolveBinding());
Why does resolveBinding() return null? How can I get the binding information?
Tucked away at the bottom of the overview of ASTParser.setKind(), carefully hidden from people troubleshooting resolveBinding() and setResolveBindings(), is the statement
Binding information is only computed when kind is K_COMPILATION_UNIT.
(from the online Javadoc)
I don't understand offhand why this would be the case, but it does seem to point pretty clearly at what needs to be different!

How to convert this code to LINQ?

I'm trying to write this as LINQ,
Original code:
For Each CurrentForm As Form In MyForms
AddLink(CurrentForm.GetLink())
Next
I'm a LINQ beginner, so far I'm not quite sure where to use and where not to. If in this case LINQ will do more harm then help, feel free to flame me.
Edit : You can assume that there is an overload for AddLink() which takes IEnumerable
Unless there is an overload of AddLink which takes a collection, LINQ won't avoid the loop.
Is there is such an overload then something like:
AddLinks(MyForms.Select(f => f.GetLink())
would do it.
How the above expression works (briefly):
LINQ is about expressions, taking some object (for LINQ to Objects used here, always a collection)
Select extension method takes a collection and a function and returns a collection. The function is passed each element of the input collection. And then Select returns the collection made up of all the function return values.
I have used a lambda expression to create an anonymous function that takes one argument called f (its type will be determined by the compiler) and returns the value of the expression (now corrected).
AddLinks is an assumed variant of your AddLink which takes a collection of links.
There is a lot going on, this is one of the advantages of LINQ, it is a compact way of expressing data manipulation without the usual overheads of explicit loops and temporary variables.
No flames here, but LINQ won't really help here. If LINQ had a ForEach method (as has been discussed in a previous question, as well as elsewhere) then you could use that - but it's not built into LINQ, and in this case there doesn't really seem to be much use for it.
Of course, it depends exactly what AddLink does - if it adds a link to a list, and you could instead use (say) List.AddRange, then you could use LINQ. But this code seems pretty simple and readable already, so I wouldn't worry in this case.

Lambdas with captured variables

Consider the following line of code:
private void DoThis() {
int i = 5;
var repo = new ReportsRepository<RptCriteriaHint>();
// This does NOT work
var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();
// This DOES work
var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();
}
So when I hardwire an actual number into the lambda function, it works fine. When I use a captured variable into the expression it comes back with the following error:
No mapping exists from object type
ReportBuilder.Reporter+<>c__DisplayClass0
to a known managed provider native
type.
Why? How can I fix it?
Technically, the correct way to fix this is for the framework that is accepting the expression tree from your lambda to evaluate the i reference; in other words, it's a LINQ framework limitation for some specific framework. What it is currently trying to do is interpret the i as a member access on some type known to it (the provider) from the database. Because of the way lambda variable capture works, the i local variable is actually a field on a hidden class, the one with the funny name, that the provider doesn't recognize.
So, it's a framework problem.
If you really must get by, you could construct the expression manually, like this:
ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
Expression.Lambda<Func<RptCriteriaHint,bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
x,
typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
Expression.Constant(i)),
x)).ToList();
... but that's just masochism.
Your comment on this entry prompts me to explain further.
Lambdas are convertible into one of two types: a delegate with the correct signature, or an Expression<TDelegate> of the correct signature. LINQ to external databases (as opposed to any kind of in-memory query) works using the second kind of conversion.
The compiler converts lambda expressions into expression trees, roughly speaking, by:
The syntax tree is parsed by the compiler - this happens for all code.
The syntax tree is rewritten after taking into account variable capture. Capturing variables is just like in a normal delegate or lambda - so display classes get created, and captured locals get moved into them (this is the same behaviour as variable capture in C# 2.0 anonymous delegates).
The new syntax tree is converted into a series of calls to the Expression class so that, at runtime, an object tree is created that faithfully represents the parsed text.
LINQ to external data sources is supposed to take this expression tree and interpret it for its semantic content, and interpret symbolic expressions inside the tree as either referring to things specific to its context (e.g. columns in the DB), or immediate values to convert. Usually, System.Reflection is used to look for framework-specific attributes to guide this conversion.
However, it looks like SubSonic is not properly treating symbolic references that it cannot find domain-specific correspondences for; rather than evaluating the symbolic references, it's just punting. Thus, it's a SubSonic problem.