2

Apologies if the title is a little unclear but I'm not entirely sure what the correct terminology is.

I've written a custom LINQ provider to generate query strings against a search provider based on a lambda. This works fine as long as there's a 1:1 property:field mapping. However, I've now been asked to modify it so that when a certain properties are referenced, it generates an OR and checks multiple fields.

So instead of function(x) x.CreatedDate = #1 Jan 2012# generating ("CreatedDate" : "1 Jan 2012"), it should now generate (("CreatedDate" : "1 Jan 2012" OR "CreatedOn" : "1 Jan 2012"))

I've annotated my entity so I can determine which alternate fields to check:

Public Class MyEntity
    <AlsoKnownAs("CreatedOn")>
    Public Property CreatedDate as Date
End Class

But where I'm struggling is how I can modify my expression visitor so that it generates the correct terms. Currently I do this...

Protected Overrides Function VisitMember(m As MemberExpression) As Expression
    If m.Expression IsNot Nothing AndAlso m.Expression.NodeType = ExpressionType.Parameter Then
        sb.Append("""")
        sb.Append(m.Member.Name)
        sb.Append("""")
        Return m
    End If
    Throw New NotSupportedException(String.Format("The member '{0}' is not supported", m.Member.Name))
End Function

I can detect the custom attributes at this point but I'm now down to the single member being evaluated, not the expression and I actually need to duplicate the parent node (the equals) a number of times.

How should I be approaching this?

To provide some more code, here's my

Protected Overrides Function VisitBinary(b As BinaryExpression) As Expression
    Select Case b.NodeType
            ....
        Case ExpressionType.Equal
            If b.Left.NodeType = ExpressionType.Call AndAlso
                DirectCast(b.Left, MethodCallExpression).Method.DeclaringType = GetType(Microsoft.VisualBasic.CompilerServices.Operators) AndAlso
                DirectCast(b.Left, MethodCallExpression).Method.Name = "CompareString" Then
                'Cope with the the VB Pain-In-The-Ass string comparison handling
                Me.Visit(b.Left)
            Else
                'Carry on
                Me.Visit(b.Left)
                sb.Append(" : ")
                Me.Visit(b.Right)
            End If
            Exit Select
Basic
  • 26,321
  • 24
  • 115
  • 201
  • I wonder if it can be done without actually modifying Unary/Binary expressions visitors...you don't want to follow this way? – Bartosz Sep 13 '12 at 10:06
  • @Bartosz If I understand you correctly, I have no real objection but I've already got an issue due to the way VB handles string comparisons (See [this Q](http://stackoverflow.com/questions/12197569)), but if you can see a convenient way to do it, I'd be interested – Basic Sep 13 '12 at 10:13
  • Nothing more convenient is coming to my mind than just having special case with checking MethodDeclaringType and Method name and then just call VisitBinaryExpression with some Expression created from arguments (`Expression.Create(...)`) – Bartosz Sep 13 '12 at 10:19

1 Answers1

1

It seems to me you need to modify code for expression visitors that are affected by this (those that evaluate to bool I assume):

  • VisitBinaryExpression when Expression is Expression.Equal
  • the case when your field is being evaluated to bool, you can substitute fieldname OR `fieldname2' (I don't know the specifics of your solution).

From the code you've posted, I could do something like: * for VB-comparison case I would just create new Expression, and Visit it with another visitor that would handle aliases:

expr = Expression.Equal(left, right) // left and right I would fetch from MethodCallExpression Arguments property
VisitEqual(expr)

and then VisitEqual should just build OR expressions basing on the aliases.

Bartosz
  • 3,318
  • 21
  • 31
  • My "Query String" is actually a tree so the structure shouldn't present too much of a problem. I'll post an example of my binary comparison so you can see what I'm working with – Basic Sep 13 '12 at 10:14
  • Thanks, that sounds promising, let me have a play with it – Basic Sep 13 '12 at 10:45
  • Sorry that my answers cannot be more detailed - I've got almost no VB knowledge, and I was playing with custom query providers only one time, some years ago... – Bartosz Sep 13 '12 at 10:54
  • I understand completely, no need to apologise. I'm getting close. The problem I'm hitting is that if I use change the `VisitBinary` functionality to convert `A=Y` into `A=Y OR B=Y` then visit it, I'm getting endless recursion (`A=Y OR B=Y` evaluates into `A=Y OR B=Y OR B=Y` etc.. etc..) Is there any way I can conveniently flag the specific occurence of this property as expanded? – Basic Sep 13 '12 at 11:30
  • Hah, true. Don't have any idea at the moment, let me know when you come up with something. – Bartosz Sep 13 '12 at 11:33