Search code examples
vb.netlinqexpression-trees

expression.call value cannot be null


I'm trying to code this LINQ query with expression trees:

Result = Result.Where(Function(Row) Convert.ToInt32(Row(2)) <= 10)

Result is declared as Dim Result As IEnumerable(Of Object()).

I have this code so far:

Dim queryiabledata As IQueryable(Of Object()) = Result.AsQueryable

Dim pe As ParameterExpression = Expression.Parameter(GetType(String), "Row(2)")
Dim left As expression = Expression.Call(pe, GetType(String).GetMethod("Convert.ToInt32", System.Type.EmptyTypes))
Dim right As Expression = Expression.Constant(10)
Dim e1 As Expression = Expression.LessThanOrEqual(left, right)

Dim predicatebody As Expression = e1
Dim wherecallexpression As MethodCallExpression = Expression.Call(
    GetType(Queryable), "Where", New Type() {queryiabledata.ElementType}, queryiabledata.Expression,
    Expression.Lambda(Of Func(Of Object(), Boolean))(predicatebody, New ParameterExpression() {pe}))

Result = queryiabledata.Provider.CreateQuery(Of Object())(wherecallexpression)

But if I run the query, I get an ArgumentNullException (Value cannot be null. Parameter name: method) at Expression.Call.

I tried to change "Convert.ToInt32" to "Value", but I got the same error.

How can I fix it?

Are the another code lines right to get the desired result?


Solution

  • I'm more used to C#, though I do VB.NET occasionally. Reflection in VB.NET is downright ugly. Getting the Where method is a bit of a hack. Here's the code:

    shortForm and longForm should be identical.

        Dim result As IEnumerable(Of Object()) = New List(Of Object())()
        Dim queryiabledata As IQueryable(Of Object()) = result.AsQueryable()
        Dim shortForm As Expression = queryiabledata.Where(Function(Row) Convert.ToInt32(Row(2)) <= 10).Expression
    
        Dim whereMethod = GetType(Queryable).GetMethods(BindingFlags.Public Or BindingFlags.Static).
            First(Function(m) m.Name = "Where").
            MakeGenericMethod(GetType(Object()))
        Dim convertMethod = GetType(System.Convert).GetMethod("ToInt32", New Type() {GetType(Object)})
        Dim rowParameter = Expression.Parameter(GetType(Object()), "Row")
    
        Dim longform As Expression =
            Expression.Call(
                whereMethod,
                queryiabledata.Expression,
                Expression.Lambda(
                    Expression.LessThanOrEqual(
                        Expression.Call(
                                convertMethod,
                                Expression.ArrayAccess(
                                    rowParameter,
                                    Expression.Constant(2)
                                )
                            ),
                        Expression.Constant(10)
                    ),
                    rowParameter
                )
            )
        result = queryiabledata.Provider.CreateQuery(longform)