Question

I'm trying to create a helper class to easily build dynamic LINQ queries. Code is heavily modified version of this excellent CodeProject article: http://www.codeproject.com/Articles/493917/Dynamic-Querying-with-LINQ-to-Entities-and-Express which uses the PredicateBuilder here: http://www.albahari.com/nutshell/predicatebuilder.aspx

I'm still in the process of cleaning up the code so variables names are rubbish at the moment and there is not one comment in sight.

Usage

    Dim conditions As New List(Of Condition)
    With conditions
        .Add(New Condition With {.Field = "EatsPeople", .Operator = "Equals", .Value = False})
        .Add(New Condition With {.Field = "Name", .Operator = "Equals", .Value = "Adric"})
        .Add(New Condition With {.Field = "Description", .Operator = "Contains", .Value = "ugly"})
    End With

    Dim predicate = BuildPredicate(conditions)
    Dim tester = data.Monsters.AsExpandable.Where(predicate).ToList

Entity

Public Class Monster
    Public Property EatsPeople As Boolean
    Public Property Name As String
    Public Property Description As String
End Class

Storage for search:

Public Class Condition
    Public Property Field As String
    Public Property [Operator] As String
    Public Property Value As Object
End Class

Methods

Public Function BuildPredicate(conditions As List(Of Condition)) As Expression(Of Func(Of Monster, Boolean))

    Dim predicate = PredicateBuilder.True(Of Monster)()
    For Each c In conditions
        Dim dbFieldName = c.Field
        Dim dbType = GetType(Monster)
        Dim dbFieldMemberInfo = dbType.GetMember(dbFieldName, BindingFlags.IgnoreCase Or BindingFlags.Public Or BindingFlags.Instance).Single()

        predicate = BuildExpression(c, dbType, dbFieldMemberInfo, predicate)
    Next
    Return predicate

End Function

Private Function CreateLambda(value As Object, method As MethodInfo, ByVal dbType As Type, ByVal dbFieldMemberInfo As MemberInfo) As Expression(Of Func(Of Monster, Boolean))

    Dim dbTypeParameter = Expression.Parameter(dbType, "x")
    Dim dbFieldMember = Expression.MakeMemberAccess(dbTypeParameter, dbFieldMemberInfo)
    Dim criterionConstant = New Expression() {Expression.Constant(value)}
    Dim containsCall = Expression.Call(dbFieldMember, method, criterionConstant)
    Return TryCast(Expression.Lambda(containsCall, dbTypeParameter), Expression(Of Func(Of Monster, Boolean)))

End Function

And the problematic method

Private Function BuildExpression(condition As Condition, ByVal dbType As Type, ByVal dbFieldMemberInfo As MemberInfo, ByVal predicate As Expression(Of Func(Of Monster, Boolean))) As Expression(Of Func(Of Monster, Boolean))

    Dim type = condition.Value.GetType
    Dim method As MethodInfo = type.GetMethod(condition.Operator, BindingFlags.Instance Or BindingFlags.Public, Nothing, {type}, Nothing)

    If type = GetType(String) Then

        If condition.Value.ToString.Contains(",") = True Then

            Dim inner = PredicateBuilder.False(Of Product)()
            For Each v In condition.Value.ToString.Split(",")
                inner = inner.Or(CreateLambda(v, method, dbType, dbFieldMemberInfo))
            Next     
            *****Return predicate.And(inner)*****
        End If

    End If

    Return predicate.And(CreateLambda(condition.Value, method, dbType, dbFieldMemberInfo))

End Function

What is the problem?

The line in the last method surrounded by five asterisks is the issue.

When adding the OR grouped expression to the original predicate builder you get

The parameter 'f' was not bound in the specified LINQ to Entities query expression.

For some reason, I can't seem to create a nested expression. If you put the code above where the query is executed in usage sample, it works (after making some mods).

Update with solution:

The line with the asterisks needs to change to Return predicate.And(inner.Expand)

Était-ce utile?

La solution

I'm not a Visual Basic person, but in the C# implementation I think you'd probably need to add AsExpandable() to your query as per albahari.com/nutshell/predicatebuilder.aspx.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top