Search code examples
c#functionlambdadelegatesexpression

Call method directly - MakeGenericMethod


I wrote the code below using this answer: LINQ to Entities does not recognize the method 'Method name' method

    /// <summary>
    /// Helper class to assit with ordering
    /// </summary>
    public static class QueryableHelper
    {
        public static IOrderedQueryable<TModel> OrderBy<TModel>(this IQueryable<TModel> q, string name, string orderType)
        {
            Type entityType = typeof(TModel);
            PropertyInfo p = entityType.GetProperty(name);
            MethodInfo m = typeof(QueryableHelper).GetMethod(orderType).MakeGenericMethod(entityType, p.PropertyType);
            return (IOrderedQueryable<TModel>)m.Invoke(null, new object[] { q, p });
        }

        public static IOrderedQueryable<TModel> OrderByPropertyDescending<TModel, TRet>(IQueryable<TModel> q, PropertyInfo p)
        {
            var expression = GetExpression<TModel, TRet>(q, p);
            return q.OrderByDescending(expression);
        }

        public static IOrderedQueryable<TModel> OrderByProperty<TModel, TRet>(IQueryable<TModel> q, PropertyInfo p)
        {
            var expression = GetExpression<TModel, TRet>(q, p);
            return q.OrderBy(expression);
        }

        private static Expression<Func<TModel, TRet>> GetExpression<TModel, TRet>(IQueryable<TModel> q, PropertyInfo p)
        {
            ParameterExpression pe = Expression.Parameter(typeof(TModel));
            Expression se = Expression.Convert(Expression.Property(pe, p), typeof(TRet));
            var expression = Expression.Lambda<Func<TModel, TRet>>(se, pe);
            return expression;
        }
    }    

I have removed some of the repetition, but there is still more I can remove.

I want to call this function directly:

OrderByPropertyDescending

Currently it is being called by this line:

MethodInfo m = typeof(QueryableHelper).GetMethod(orderType).MakeGenericMethod(entityType, p.PropertyType);

Is there a way to call this function directly eg like this:

var res = OrderByPropertyDescending<TModel, TRet>(q, p);

My problem is that I do not know what to pass in for TRet

Any ideas?


Solution

  • You can use Expression<Func<TModel, TProperty>> as a type for property instead of PropertyInfo.

    That will result in calls like: OrderByPropertyDescending(model, model => model.X), where all generic arguments are automatically figured.

    Then, you'll need to fetch PropertyInfo from given expression, so you'll just need to check type of given expression.

    I've seen such an idea in Caliburn.Micro - see, for example, their ExpressionExtensions: https://github.com/Caliburn-Micro/Caliburn.Micro/blob/master/src/Caliburn.Micro.Core/ExpressionExtensions.cs

    Actually, their GetMemberInfo will work for you.