【问题标题】:Implementing string comparer in custom Linq Provider in VB在VB的自定义Linq Provider中实现字符串比较器
【发布时间】:2012-08-25 01:29:28
【问题描述】:

我正在尝试关注this series of articles。我在第 2 部分和第 3 部分之间,但遇到了一些问题。

我正在用 VB.Net 编写代码,这引发了一些怪癖。

具体来说,当访问表达式树时,字符串比较没有按预期工作。

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

为字符串比较抛出 NotImplementedException

m.Method.DeclaringTypeMicrosoft.VisualBasic.CompilerServices.Operators 类型,m.Method.NameCompareString。看起来 VB 对字符串相等的处理方式略有不同,并且没有以正确的方式进行处理。

我正在使用Query.Where(function(x) x.Content_Type <> "") 进行测试。

具体来说,如果我调试对VisitBinary(b As BinaryExpression)(也称为Part 2)的调用,b 就是{(CompareString(x.Content_Type, "", False) != 0)}

然后它会尝试访问b.Left (CompareString(x.Content_Type, "", False)),这是我们从VisitMethodCall 的漏洞中跌落的地方。

如果我只是将 VisitMethodCall 中的 If 展开为

    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

它在尝试将 StripQuotes(m.Arguments(1)) 转换为 LambdaExpression 时抛出 InvalidCastException(说它是 ConstantExpression

我需要做什么才能在 VB 中正确处理字符串比较?

【问题讨论】:

  • @DanielHilgarth tbh 我是not convinced you're correct (lambas 可以用来代替匿名函数/代表,但不是必须的)但无论哪种方式,这个 QueryProvider 应该能够遍历一个很可能包含 lambdas 的表达式树并将其转换为查询字符串以在其他地方使用。

标签: .net vb.net linq provider


【解决方案1】:

使用以下类:

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

现在如果你有一个来自 vb 编译器的表达式,例如

Dim expressionFromVb As Expression(Of Func(Of String, Boolean)) = Function(x) x = "b"

结构为 {(CompareString(x, "b", False) == 0)} 使用

Dim newExpression = New VbCompareReplacer().Visit(expressionFromVb)

而不是 expressionFromVb,它的结构是 {x => (x == "b")}。所有凌乱的 vb 比较都被预期的(不)等式比较所取代。如果您将newExpression 放入为 c# 编写的 linq 提供程序中,它现在应该可以正常工作了。

【讨论】:

  • +1 这是一个非常优雅的解决方案。我明天上班的时候试一下,看看效果如何,谢谢。
猜你喜欢
  • 2011-12-26
  • 1970-01-01
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多