问题
I'm attempting to follow this series of articles. I'm between parts 2 and 3 but am having some issues.
I'm writing the code in VB.Net which has thrown a couple of quirks.
Specifically, when visiting the expression tree, string comparisons aren't working as expected.
This method in the QueryProvider (Part 2)
Protected Overrides Function VisitMethodCall(m As MethodCallExpression) As Expression
If m.Method.DeclaringType = GetType(Queryable) AndAlso m.Method.Name = "Where" Then
sb.Append("SELECT * FROM (")
Me.Visit(m.Arguments(0))
sb.Append(") AS T WHERE ")
Dim lambda As LambdaExpression = DirectCast(StripQuotes(m.Arguments(1)), LambdaExpression)
Me.Visit(lambda.Body)
Return m
End If
Throw New NotSupportedException(String.Format("The method '{0}' is not supported", m.Method.Name))
End Function
Is throwing a NotImplementedException for String comparisons
m.Method.DeclaringType
is of type Microsoft.VisualBasic.CompilerServices.Operators
and m.Method.Name
is CompareString
. It looks like the string equality is handled slightly differently by VB and isn't being picked up in the correct way.
I'm using a Query.Where(function(x) x.Content_Type <> "")
to test.
Specifically, if I debug the calls to VisitBinary(b As BinaryExpression)
(Also Part 2), b
is {(CompareString(x.Content_Type, "", False) != 0)}
This then attempts to visit b.Left
(CompareString(x.Content_Type, "", False)
) which is where we fall through the hole in VisitMethodCall
.
If I just expand the If in VisitMethodCall to be
If (
m.Method.DeclaringType = GetType(Queryable) AndAlso
m.Method.Name = "Where"
) Or (
m.Method.DeclaringType = GetType(Microsoft.VisualBasic.CompilerServices.Operators) AndAlso
m.Method.Name = "CompareString") Then
It throws an InvalidCastException on the trying to convert the StripQuotes(m.Arguments(1))
to LambdaExpression
(says it's a ConstantExpression
)
What do I need to do to handle string comparisons correctly from VB?
回答1:
Use the following class:
Imports System.Linq.Expressions
Public Class VbCompareReplacer
Inherits ExpressionVisitor
Public Overrides Function Visit(node As Expression) As Expression
If Not TypeOf node Is BinaryExpression Then Return MyBase.Visit(node)
Dim binaryExpression = DirectCast(node, BinaryExpression)
If Not TypeOf binaryExpression.Left Is MethodCallExpression Then Return MyBase.Visit(node)
Dim method = DirectCast(binaryExpression.Left, MethodCallExpression)
If Not (method.Method.DeclaringType = GetType(Microsoft.VisualBasic.CompilerServices.Operators) AndAlso
method.Method.Name = "CompareString") Then Return MyBase.Visit(node)
Dim left = method.Arguments(0)
Dim right = method.Arguments(1)
Return If(binaryExpression.NodeType = ExpressionType.Equal,
Expression.Equal(left, right),
Expression.NotEqual(left, right))
End Function
End Class
Now if you have an expression from the vb compiler, for example
Dim expressionFromVb As Expression(Of Func(Of String, Boolean)) = Function(x) x = "b"
which has the structure {(CompareString(x, "b", False) == 0)} use
Dim newExpression = New VbCompareReplacer().Visit(expressionFromVb)
instead of expressionFromVb
which has the structure {x => (x == "b")}. All messy vb comparisons are replaced by the expected (in-)equality comparisons. If you put newExpression
into your linq provider written for c#, it should work properly now.
来源:https://stackoverflow.com/questions/12194855/implementing-string-comparer-in-custom-linq-provider-in-vb