Creating Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> dynamically?
Asked Answered
E

1

6

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)} , .... ?

Eyetooth answered 4/12, 2014 at 12:3 Comment(2)
Can you explain what you need that for? Possibly there is a much easier solution to your "bigger problem"...Besetting
I created SortingItems in client app and send it to service. In Service I wanna create the sorting func to use in entity frameworkEyetooth
A
3

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);
Auer answered 4/12, 2014 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.