Using brackets in dynamic .NET expressions

2019-07-10 07:27发布

问题:

I have a grid in witch a user can fill-in the "filter" on a collection. The user has to fill-in some columns:

AndOr  Property  Comparator  Value

say, for a Cities collection it could filter cities that

   -  Name       StartsWith  'a'
  AND Population     >       10000
  OR  Population     <       1000

I used the dynamic PredicateBuilder, that worked very well, until the "brackets" requirement appeared.

As you can see from the "query" above, in the resulting collection we will have cities which
(Name.StartsWith'a' AND Population > 10000) OR (Population < 1000).

In order to build the expression
Name.StartsWith'a' AND (Population > 10000 OR (Population < 1000) I need to use some brackets.

Now, the filter grid columns changed to

AndOr  LeftBracket  Property  Comparator  Value  RightBracket

Is there some "Group", "Open/CloseBracket" in the .NET Dynamic Expression library? An other way to realize it?

The code to "link" rows between them was the following

Private Function GetMyObjectsDataSource() As IQueryable(Of MyObject)
  ' start without any filter, get it all '
  Dim predicate = PredicateBuilder.True(Of MyObject)()
  Dim filterExpression As Expression(Of Func(Of MyObject, Boolean)) = predicate

  For Each row In grdFilter.Rows
    Dim rowExpression = GetExpressionFromRow(Of MyObject)(row)
    Dim compOp As LogicalOperator = row.Cells(ColumnKeys.AndOr).Value

    If compOp = LogicalOperator.Or Then
      filterExpression = [Or](filterExpression, rowExpression)
    Else
      filterExpression = [And](filterExpression, rowExpression)
    End If
  Next row

  Dim myObjects As List(Of MyObject) = Me._Container.GetMyObjects()
  Dim result As IQueryable(Of MyObject) =
    myObjects.AsQueryable().Where(filterExpression)

  Return result
End Function

回答1:

The best way to deal with this is to use nested sub-expressions in your expression tree. This likely involves changing the way you are interpreting the user's input. Basically whenever you encounter a LeftBracket element, you would recursively build a sub-expression up to the next RightBracket element in the current scope. Then you would assign that entire sub-expression as a node in the current operation.

From reviewing the code sample in your question, I would suspect the best place to do this is in the "GetExpressionFromRow" function. Unfortunately, I don't think the "GetExpressionFromRow" is part of the code library you referenced in your question.

If you can update your question to include the GetExpressionFromRow and dependencies, I can try to examine further and give a more specific answer.

The basic algorithm behind this technique is called a recursive descent parser. See some general information here: http://en.wikipedia.org/wiki/Recursive_descent_parser