Order by enum description

前端 未结 6 1600
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-13 10:55

I am working on an ASP.NET MVC projet using EF code first, and I am facing a situation where I need to order by an enum description:

public partial class Ite         


        
6条回答
  •  暖寄归人
    2021-01-13 11:17

    I had a similar problem to solve, only that my ordering had to be dynamic, that is the sort by column parameter is a string.

    The boolean sorting also had to be customized in that sense that true comes before false (e.g. 'Active' is before 'Inactive').

    I'm sharing here the complete code with you, so you can spare your time. In case you find spots for improvement, please feel free to share in a comment.

    private static IQueryable OrderByDynamic(this IQueryable query, SortField sortField)
    {
        var queryParameterExpression = Expression.Parameter(typeof(T), "x");
        var orderByPropertyExpression = GetPropertyExpression(sortField.FieldName, queryParameterExpression);
    
        Type orderByPropertyType = orderByPropertyExpression.Type;
        LambdaExpression lambdaExpression = Expression.Lambda(orderByPropertyExpression, queryParameterExpression);
    
        if (orderByPropertyType.IsEnum)
        {
            orderByPropertyType = typeof(int);
            lambdaExpression = GetExpressionForEnumOrdering(lambdaExpression);
        }
        else if (orderByPropertyType == typeof(bool))
        {
            orderByPropertyType = typeof(string);
            lambdaExpression =
                GetExpressionForBoolOrdering(orderByPropertyExpression, queryParameterExpression);
        }
    
        var orderByExpression = Expression.Call(
            typeof(Queryable),
            sortField.SortDirection == SortDirection.Asc ? "OrderBy" : "OrderByDescending",
            new Type[] { typeof(T), orderByPropertyType },
            query.Expression,
            Expression.Quote(lambdaExpression));
    
        return query.Provider.CreateQuery(orderByExpression);
    }
    

    The shared GetPropertyExpression has been simplified a bit, to exclude the nested property handling.

    private static MemberExpression GetPropertyExpression(string propertyName, ParameterExpression queryParameterExpression)
    {
        MemberExpression result = Expression.Property(queryParameterExpression, propertyName);
        return result;
    }
    

    Here is the slightly modified code (from the accepted solution) to handle the Enum ordering.

    private static Expression> GetExpressionForEnumOrdering(LambdaExpression source)
    {
        var enumType = source.Body.Type;
        if (!enumType.IsEnum)
            throw new InvalidOperationException();
    
        var body = ((int[])Enum.GetValues(enumType))
            .OrderBy(value => GetEnumDescription(value, enumType))
            .Select((value, ordinal) => new { value, ordinal })
            .Reverse()
            .Aggregate((Expression)null, (next, item) => next == null ? (Expression)
                Expression.Constant(item.ordinal) :
                Expression.Condition(
                    Expression.Equal(source.Body, Expression.Convert(Expression.Constant(item.value), enumType)),
                    Expression.Constant(item.ordinal),
                    next));
    
        return Expression.Lambda>(body, source.Parameters[0]);
    }
    

    And the boolean ordering as well.

    private static LambdaExpression GetExpressionForBoolOrdering(MemberExpression orderByPropertyExpression, ParameterExpression queryParameterExpression)
    {
        var firstWhenActiveExpression = Expression.Condition(orderByPropertyExpression,
            Expression.Constant("A"),
            Expression.Constant("Z"));
    
        return Expression.Lambda(firstWhenActiveExpression, new[] { queryParameterExpression });
    }
    

    Also the GetEnumDescription has been modified to receive the Type as the parameter, so it can be called without a generic.

    private static string GetEnumDescription(int value, Type enumType)
    {
        if (!enumType.IsEnum)
            throw new InvalidOperationException();
    
        var name = Enum.GetName(enumType, value);
        var field = enumType.GetField(name, BindingFlags.Static | BindingFlags.Public);
        return field.GetCustomAttribute()?.Description ?? name;
    }
    

    The SortField is a simple abstraction containing the string column property to be sorted upon and the direction of the sort. For the sake of simplicity I am also not sharing that one here.

    Cheers!

提交回复
热议问题