0

我正在 VB.NET 中创建一个生成 SQL 字符串的库。然而,生成WHERE子句(和类似的结构)给我带来了问题。我已经确定最简单的 where 子句结构本质上是

expression1 [AND expression2 [AND expressionN]]

这很容易用Expression对象列表在代码中表示 - 然后程序只需遍历每个对象,调用其覆盖的方法并每次ToString()附加(最后一个除外)。AND简单的。当OR考虑到复杂性时: where 子句的结构变成

expression1 [[AND|OR] expression2 [[AND|OR] expressionN]]

我现在需要既能保存所有的表达式,又能跟踪每个表达式是否与下一个分隔为ANDor OR- 一个本质上的集合(expr1, andOr1, expr2, andOr2, ... exprN, andOrN)

我还考虑过结构也可以嵌套,例如:

(expression1 [[AND|OR] expression2]) [AND|OR] (expression3 [[AND|OR] expression4])

每个带圆括号的一半本身就是一个表达式。所以我认为解决方案是通过Expression进一步扩展类来实现,这样一个表达式可以是一个表达式链:我有一个新类ExpressionFromChainOfExpressions

Public Class ExpressionFromChainOfExpressions
    Inherits Expression

    Private ExprChain As List(Of Expression)

    Public Sub AddExpression(Expr As Expression)
         If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
         ExprChain.Add(Expression)
    End Sub

    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= Expr.ToString()
            If i < ExprChain.Count - 1
                'ToDo: Determine [AND|OR] and append
                'OLD: outStr &= " AND "
            End If
        Next
        Return outStr & ")"
    End Function
End Class

And将/Or作为布尔标志或类似的东西捆绑到Expression类中感觉是错误的 -Expression应该可以在其他地方重用(它们也出现在其他情况下,例如case when expression [[and|or] expression] ..., 并且And/Or本身不是表达式的一部分。

解决方案是 3 行集合技巧还是全新的类家族都没有关系。可维护性和可扩展性很重要——它不能是一个hack。如果有人知道解决此类问题的设计模式,我会非常有兴趣看到它。


编辑:根据要求,一些示例代码只是使用And(由于实际移动部件的数量,从实际代码中大大简化了):

Public MustInherit Class Expression
    Public MustOverride Function ToString() As String
End Class


Public Class ExpressionFromString
    Inherits Expression

    Private ExprString As String

    Public Sub New(ExprString As String)
        Me.ExprString = ExprString
    End Sub

    Public Overrides Function ToString() As String
        Return ExprString
    End Function
End Class


Public Class ExpressionFromChainOfExpressions
    Inherits Expression

    Private ExprChain As List(Of Expression)

    Public Sub AddExpression(Expr As Expression)
         If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
         ExprChain.Add(Expression)
    End Sub

    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= Expr.ToString()
            If i < ExprChain.Count - 1
                outStr &= " AND "
            End If
        Next
        Return outStr & ")"
    End Function
End Class

主要方法:

Public Module Module1
    Dim myExpr1 = New ExpressionFromString("A = B")
    Dim myExpr2 = New ExpressionFromString("C > 100")
    dIM myExpr3 = New ExpressionFromString("D Is Null")

    Dim myNestedChain = New ExpressionFromChainOfExpressions()
    myExprChain.Add(myExpr1)
    myExprChain.Add(myExpr2)
    Console.WriteLine("WHERE " & myNestedChain.ToString() )

    Dim myOuterChain = New ExpressionFromChainOfExpressions()
    myOuterChain.Add(myNestedChain)
    myOuterChain.Add(myExpr3)

    Console.WriteLine("WHERE " & myOuterChain.ToString() )
End Module

输出:

WHERE (A = B AND C > 100)
WHERE ((A = B AND C > 100) AND D Is Null)
4

2 回答 2

1

这里有一个关于如何面对你的问题的建议:

Public Class ExpressionFromChainOfExpressions
    Inherits Expression

    Private ExprChain As List(Of Expression)
    Private ExprConn As List(Of Connector)
    Public Enum Connector
        AndConn
        OrConn
    End Enum
    Public Sub AddExpression(Expr As Expression) 'Might have to be changed to account for connectors
        If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
        ExprChain.Add(Expr)
    End Sub
    Public Overrides Function ToString() As String
        Dim outStr As String = "("
        For i = 0 To ExprChain.Count - 1
            outStr &= ExprChain(i).ToString()
            If i < ExprChain.Count - 1 Then
                outStr &= 'will account for the given connector (as stored in ExprConn)
            End If
        Next
        Return outStr & ")"
    End Function
End Class

等效地,当您定义 inputExpressions时,定义 input Connectors(aPublic Enum包括您想要的所有替代项(暂时为“And”和“Or”);请记住,“And”和“Or”不能使用,因此必须想出等价的名字)。通过遵循您当前版本的想法Class,您可能会包含一个AddConnector函数,尽管这会使一切变得过于混乱(每次两次调用,难以将表达式与连接器相关联等)。我建议您更改 AddExpression ,使其至少接受两个参数:表达式和连接器(指先前输入的表达式或下一个);通过这种方式,您将能够填充两个列表(ExprChainExprConn) 并行并让它们完美协调以用于ToString().

于 2013-08-23T10:23:36.303 回答
0

我自己对 varocarbas 解决方案的变体,允许通过布尔值更好地控制嵌套IsNested,并且不依赖于两个集合的排序:

Public Class ExpressionFromChainOfExpressions
    Inherits Expression

    Private ExprChain As List(Of Expression)

    'Alternatively use an Enum as per varocarbas's solution 
    'for better consistency control
    Private ChainWord As String

    'If true, wraps ( ) around ToString()'s output
    Private IsNested As Boolean

    Public Sub New(ByVal ChainWord As String, ByVal IsNested As Boolean)
        Me.ChainWord = ChainWord
        Me.IsNested = IsNested
    End Sub

    Public Sub AddExpression(ByVal Expr As Expression)
        If ExprChain Is Nothing Then ExprChain = New List(Of Expression)
        ExprChain.Add(Expr)
    End Sub

    Public Overrides Function ToString() As String
        Dim outStr As String = ""
        If IsNested Then outStr = "("

        For i = 0 To ExprChain.Count - 1
            outStr &= ExprChain(i).ToString()
            If i < ExprChain.Count - 1 Then
                outStr &= String.Format(" {0} ", ChainWord)
            End If
        Next

        If IsNested Then outStr = ")"

        Return outStr
    End Function
End Class

允许复杂的输出,例如

expr1 AND expr2 OR (exp3 AND (expr4 OR expr5 AND expr6 AND expr7))
于 2013-08-23T11:40:52.887 回答