Search code examples
c#iqueryablelinq-expressionsiorderedqueryable

Dynamic Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> Expression


I am using patterns mentioned here http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

And i am using method below to query EF

public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

Now i want to create dynamic Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> expression to order my data.

I know only field name as string and order type (ascending, descending) as string (asc, desc)


Solution

  • finally i could write the method i want.

     public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderBy(string orderColumn, string orderType) {
                Type typeQueryable = typeof(IQueryable<TEntity>);
                ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p");
                var outerExpression = Expression.Lambda(argQueryable, argQueryable);
                string[] props = orderColumn.Split('.');
                IQueryable<TEntity> query = new List<TEntity>().AsQueryable<TEntity>();
                Type type = typeof(TEntity);
                ParameterExpression arg = Expression.Parameter(type, "x");
    
                Expression expr = arg;
                foreach(string prop in props) {
                    PropertyInfo pi = type.GetProperty(prop, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    expr = Expression.Property(expr, pi);
                    type = pi.PropertyType;
                }
                LambdaExpression lambda = Expression.Lambda(expr, arg);
                string methodName = orderType == "asc" ? "OrderBy" : "OrderByDescending";
    
                MethodCallExpression resultExp =
                    Expression.Call(typeof(Queryable), methodName, new Type[] { typeof(TEntity), type }, outerExpression.Body, Expression.Quote(lambda));
                var finalLambda = Expression.Lambda(resultExp, argQueryable);
                return (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)finalLambda.Compile();
            }
    

    This method takes two parameters, first one field name other one is asc or desc. Result of method can be used directly with IQueryable object.

    Thanks for your helps