Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> dynamically?

I wanna create Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> from list of SortingItems dynamically. I mean I wanna create the following expression:

entity => entity.OrderBy(c => c.Id).ThenBy(c => c.Name).ThenByDescending(c => c.LastName)

The following is my code :

[DataContract]
public class SortingItem
{
    [DataMember]
    public string PropertySelectorString { get; set; }

    [DataMember]
    public SortingDirectionsEnum SortingDirections { get; set; }
}

[DataContract]
public enum SortingDirectionsEnum
{
    [EnumMember]
    Descending = 0,

    [EnumMember]
    Ascending = 1
}

public Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetSortingFunc<TEntity>()
{
    _entityType = typeof(TEntity);
    _parameterExpression = Expression.Parameter(_entityType, "entity");
    _iQueryableParameterExpression = Expression.Parameter(typeof(IQueryable<TEntity>), "f");

    MethodInfo orderByMethodInfo = null;
    Expression resultExpression = null;

    foreach (SortingItem sortingItem in SortingItems)
    {
        MemberExpression memberExpression = GetLeftSide(sortingItem.PropertySelectorString, _entityType, _parameterExpression); // I'm dead sure about working this line

        switch (sortingItem.SortingDirections)
        {
            case SortingDirectionsEnum.Descending:
                orderByMethodInfo = typeof(Queryable).GetMethods().First(method => method.Name == "OrderBy" && method.GetParameters().Length == 2).MakeGenericMethod(_entityType, memberExpression.Type);
                if (resultExpression != null)
                    orderByMethodInfo = typeof(Queryable).GetMethods().First(method => method.Name == "ThenBy" && method.GetParameters().Length == 2).MakeGenericMethod(_entityType, memberExpression.Type);
                break;

            case SortingDirectionsEnum.Ascending:
                orderByMethodInfo = typeof(Queryable).GetMethods().First(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2).MakeGenericMethod(_entityType, memberExpression.Type);
                if (resultExpression != null)
                    orderByMethodInfo = typeof(Queryable).GetMethods().First(method => method.Name == "ThenByDescending" && method.GetParameters().Length == 2).MakeGenericMethod(_entityType, memberExpression.Type);
                break;
        }

        MethodCallExpression methodCallExpression;
        if (resultExpression != null)
            // Exception
            // An unhandled exception of type 'System.ArgumentException' occurred in System.Core.dll
            // Additional information: Incorrect number of arguments supplied for call to method 'System.Linq.IOrderedQueryable`1[ConsoleApplication1.User] ThenBy[User,Int32](System.Linq.IOrderedQueryable`1[ConsoleApplication1.User], System.Linq.Expressions.Expression`1[System.Func`2[ConsoleApplication1.User,System.Int32]])'
            methodCallExpression = Expression.Call(orderByMethodInfo, _iQueryableParameterExpression, resultExpression, Expression.Lambda(memberExpression, _parameterExpression)); 
        else
            methodCallExpression = Expression.Call(orderByMethodInfo, _iQueryableParameterExpression, Expression.Lambda(memberExpression, _parameterExpression));

        resultExpression = Expression.Lambda(methodCallExpression, _iQueryableParameterExpression);
    }

    Expression<Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>> lambdaExpression = Expression.Lambda<Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>>(resultExpression, _parameterExpression);
    return lambdaExpression.Compile();
}

It's OK to create {f.OrderBy(entity => entity.Id)} or {f.ThenBy(entity => entity.Name)} seperately, but I get exception at the following line

methodCallExpression = Expression.Call(orderByMethodInfo, _iQueryableParameterExpression, resultExpression, Expression.Lambda(memberExpression, _parameterExpression)); 

How should I use Expression.Call to combine {f.OrderBy(entity => entity.Id)} with {f.ThenBy(entity => entity.Name)} , .... ?

like image 975
Mohammad Dayyan Avatar asked Dec 04 '14 12:12

Mohammad Dayyan


1 Answers

I suggest you not to write such code, as it's hard to read. I've had to maintain those monsters in the past.

If you're actually interested in the solution, you can download DynamicLINQ, and then your query will be as:

public string GetSortCriteria(this SortingItem item){
   return string.Format("{0} {1}", item.PropertySelectorString, 
          item.SortingDirections == SortingDirectionsEnum.Descending ? 
             "DESC" : "ASC");
}

// later: 
var mergedSortCriteria= string.Join(",", 
     SortingItems.Select(item => item.GetSortCriteria());

Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> myFunc = 
      source => source.OrderBy("id " + mergedSortCriteria);
like image 101
Erti-Chris Eelmaa Avatar answered Sep 18 '22 09:09

Erti-Chris Eelmaa