Custom server-side DataTables processing: “typeof(Enumerable).GetMethod” is null

Custom server-side DataTables processing: “typeof(Enumerable).GetMethod” is null

LePatayLePatay Posts: 20Questions: 3Answers: 1

Hello everyone,
I have been struggling for days over an old (2011) piece of C# code I extracted from a DLL my boss wants me to edit.

The application queries a database with LINQ, server-side processes it and displays the data with a DataTable.
From what I gathered, the guy who wrote it had created an ASP.NET Web Forms Site in Visual Studio 2010, with .NET Framework 4.0.

He used the server-side parser from Zack Owens.
In the project I recreated in VS 2017, everything builds well, but at runtime, a bug comes from the little customizations he made to the DataTable parser: among them, the SelectProperties() function has been completely rewritten from this:

    private Expression<Func<T, List<string>>> SelectProperties
    {
        get
        {
            return value => _properties.Select
            (
                // empty string is the default property value
                prop => (prop.GetValue(value, new object[0]) ?? string.Empty).ToString()
            )
            .ToList();
        }
    }

to this:

    private Expression<Func<T, List<string>>> SelectProperties
    {
        get
        {
            var parameterExpression = Expression.Parameter(typeof(T), "value");
            
           // (Edited) The bug happens there: type_RD is null because GetMethod is not valid
            var type_RD = typeof(Enumerable).GetMethod(
                "ToList",
                new Type[] {
                    typeof(IEnumerable<string>)
                }
            );
            
            // The program crashes there (System.Null.Exception)
            var methodFromHandle = (MethodInfo)MethodBase.GetMethodFromHandle(
                type_RD
            .MethodHandle);
    
            var expressionArray = new Expression[1];
    
            var methodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(
                typeof(Enumerable).GetMethod("Select", new Type[] {
                    typeof(IEnumerable<PropertyInfo>),
                    typeof(Func<PropertyInfo, string>)
                })
            .MethodHandle);
    
            var expressionArray1 = new Expression[] {
                Expression.Field(
                    Expression.Constant(
                        this,
                        typeof(DataTableParser<T>)
                    ),
                    FieldInfo.GetFieldFromHandle(
                        typeof(DataTableParser<T>).GetField("_properties").FieldHandle,
                        typeof(DataTableParser<T>).TypeHandle
                    )
                ), null
            };
    
            var parameterExpression1 = Expression.Parameter(
                typeof(PropertyInfo),
                "prop"
            );
    
            var methodFromHandle1 = (MethodInfo)MethodBase.GetMethodFromHandle(
                typeof(PropertyInfo).GetMethod(
                    "GetValue",
                    new Type[] {
                        typeof(object),
                        typeof(object[])
                    }
                )
            .MethodHandle);
    
            var expressionArray2 = new Expression[] {
                Expression.Convert(
                    parameterExpression,
                    typeof(object)
                ),
                Expression.NewArrayInit(
                    typeof(object),
                    new Expression[0]
                )
            };
    
            var methodCallExpression = Expression.Call(
                Expression.Coalesce(
                    Expression.Call(
                        parameterExpression1,
                        methodFromHandle1,
                        expressionArray2
                    ),
                    Expression.Field(
                        null,
                        FieldInfo.GetFieldFromHandle(
                            typeof(string).GetField("Empty").FieldHandle
                        )
                    )
                ),
                (MethodInfo)MethodBase.GetMethodFromHandle(
                    typeof(object).GetMethod("ToString").MethodHandle
                ),
                new Expression[0]
            );
    
            expressionArray1[1] = Expression.Lambda<Func<PropertyInfo, string>>(
                methodCallExpression,
                parameterExpression1
            );
            expressionArray[0] = Expression.Call(
                null,
                methodInfo,
                expressionArray1
            );
    
            // Return Lambda
            return Expression.Lambda<Func<T, List<string>>>(
                Expression.Call(
                    null,
                    methodFromHandle,
                    expressionArray
                ),
                parameterExpression
            );
        }
    }

My questions:

  • How to make the SelectProperties function work?
  • What exactly is its purpose? I didn't get any of the "MethodHandle" bits ...

The only hint I have is that, when I use the original SelectProperties code, the data are in the wrong columns and the sorting causes errors 500 with some of the columns.
Here is the other customized function of this custom DataTable.cs parser:

        public FormatedList<T> Parse()
        {
        var list = new FormatedList();
        list.Import(_properties.Select(x => x.Name).ToArray());
        
        list.sEcho = int.Parse(_httpRequest[ECHO]);
        
        list.iTotalRecords = _queriable.Count();
        
        ApplySort();
        
        int skip = 0, take = 10;
        int.TryParse(_httpRequest[DISPLAY_START], out skip);
        int.TryParse(_httpRequest[DISPLAY_LENGTH], out take);

        /* (Edited)
        list.aaData = _queriable.Where(ApplyGenericSearch)
                                .Where(IndividualPropertySearch)
                                .Skip(skip)
                                .Take(take)
                                .Select(SelectProperties)
                                .ToList();
                                    
        list.iTotalDisplayRecords = list.aaData.Count;
        */
        list.aaData = _queriable.Where(ApplyGenericSearch)
                                .Where(IndividualPropertySearch)
                                .Skip(skip)
                                .Take(take)
                                .ToList()
                                .AsQueryable()
                                .Select(SelectProperties)
                                .ToList();

        list.iTotalDisplayRecords = list.iTotalRecords;

        return list;
        }

I am a beginner at C#, .NET, Linq etc., though I know quite a few other languages.
Setup: Windows 7 64, Visual Studio 2017.

Thank you for your help!

This question has an accepted answers - jump to answer

Answers

  • LePatayLePatay Posts: 20Questions: 3Answers: 1

    TL;DR: use the original parser's SelectProperties function.

    Okay, I solved this part of my problem.
    My problem was the fact that I used decompilated code, extracted from a DLL. Both .NET Reflector and JustDecompile gave very heavy portions of code, though it was just a bad decompilation of the original line.

    Eventually, I understood that my problems of "data in the wrong columns" and "Error 500 - Exception of type 'Antlr.Runtime.NoViableAltException' was thrown" didn't come from this portion of the code (still haven't figured where, though).

  • LePatayLePatay Posts: 20Questions: 3Answers: 1
    Answer ✓

    Solved it.
    Here is the explanation of misordered data in server-side processed data using Zack Owen's DataTableParser, due to the alphabetical reordering of the properties inside Entity Framework POCOs when decompiling with JustDecompile or .NET Reflector:
    https://forum.red-gate.com/discussion/80861/bugs-of-field-order-in-reflector#Comment_149618

  • allanallan Posts: 63,075Questions: 1Answers: 10,384 Site admin

    Thanks for posting back! Great to hear you've got it running now.

    Allan

This discussion has been closed.