Search code examples
c#asp.net-mvcreflectionexpression-trees

Get OrderBy method using reflection


I want to implement generic pager and filter View Model for my project and I'm stuck on getting OrderBy method using reflection. Here is what I've tried, but keep getting null for methodInfo. It seems I'm passing the wrong Type[] arguments to the GetMethod() Method, but I can't get it right.

    protected virtual Expression<Func<T, IComparable>> GetOrderByExpression()
    {
        var type = typeof(T);
        var property = type.GetProperty("DataSetName");
        var parameter = Expression.Parameter(type, "x");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
        var methodInfo = typeof(Enumerable).GetMethod("OrderBy", new Type[] { orderByExp.Body.Type });
        var predicateBody = Expression.Call(propertyAccess, methodInfo, orderByExp);
        var expression = Expression.Lambda<Func<T, IComparable>>(predicateBody, parameter);

        return expression;
    }

Solution

  • Then Enumerable.OrderBy extension method is a static method so you have to use an overload to GetMethod where you can specify BindingFlags (BindingFlags.Static | BindingFlags.Public instead of the default BindingFlags.Instance | BindingFlags.Public).

    You also have to specify two parameters to the method - syntactically it looks like there is only one parameter but because it is an extension method there is a second parameter which you have to specify.

    This is the method you want to get via reflection:

    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
      this IEnumerable<TSource> source,
      Func<TSource, TKey> keySelector
    )
    

    You need the two parameter types:

    var sourceType = typeof(IEnumerable<>).MakeGenericType(type);
    var keySelectorType = orderByExp.Compile().GetType();
    

    It seems that the only way to get a specific overload of a method with generic parameters you have to perform a search. Fortunately, there are only two overloads of Enumerable.OrderBy and the one you want is the one with two parameters:

    var genericMethodInfo = typeof(Enumerable)
      .GetMethods(BindingFlags.Static | BindingFlags.Public)
      .First(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
    var methodInfo = genericMethodInfo.MakeGenericMethod(sourceType, keySelectorType);
    

    This returns the desired MethodInfo, however you will have to modify the remaining two lines of code because this method is a static method and you have to specify a null instance in Expression.Call.