Выполнение динамических сортировок на данных EF4 - PullRequest
0 голосов
/ 03 мая 2010

Я пытаюсь выполнить динамическую сортировку данных, которые помещаю в сетку в нашем интерфейсе MVC. Поскольку MVC абстрагируется от всего остального через WCF, я создал пару вспомогательных классов и расширений, чтобы помочь с этим. Две наиболее важные вещи (слегка упрощенные) следующие:

    public static IQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
    {
        var sortedSortOptions = (from o in sortOptions
                                 orderby o.Priority ascending
                                 select o).ToList();

        var results = collection;

        foreach (var option in sortedSortOptions)
        {
            var currentOption = option;
            var propertyName = currentOption.Property.MemberWithoutInstance();
            var isAscending = currentOption.IsAscending;

            if (isAscending)
            {
                results = from r in results
                          orderby propertyName ascending 
                          select r;
            }
            else
            {
                results = from r in results
                          orderby propertyName descending 
                          select r;
            }
        }

        return results;
    }


public interface ISortOption<TModel, TProperty> where TModel : class
{
    Expression<Func<TModel, TProperty>> Property { get; set; }
    bool IsAscending { get; set; }
    int Priority { get; set; }
}

Я не дал вам реализацию для MemberWithoutInstance(), но просто поверьте мне, что она возвращает имя свойства в виде строки. : -)

Ниже приведен пример того, как я мог бы потреблять это (используя неинтересную базовую реализацию ISortOption<TModel, TProperty>):

var query = from b in CurrentContext.Businesses
            select b;

var sortOptions = new List<ISortOption<Business, object>>
                      {
                          new SortOption<Business, object>
                              {
                                  Property = (x => x.Name),
                                  IsAscending = true,
                                  Priority = 0
                              }
                      };

var results = query.ApplySortOptions(sortOptions);

Как я обнаружил с помощью этого вопроса , проблема связана с моими orderby propertyName ascending и orderby propertyName descending строками (все остальное работает, насколько я могу судить). Как я могу сделать это динамическим / общим способом, который работает должным образом?

Ответы [ 2 ]

2 голосов
/ 03 мая 2010

Вы действительно должны использовать Dynamic LINQ для этого. На самом деле вы можете просто перечислить свойства по имени, а не использовать выражение, что упрощает создание.

public static IQueryable<T> ApplySortOptions<T, TModel, TProperty>(this IQueryable<T> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class  
{    
    var results = collection;  

    foreach (var option in sortOptions.OrderBy( o => o.Priority ))  
    {  
        var currentOption = option;  
        var propertyName = currentOption.Property.MemberWithoutInstance();  
        var isAscending = currentOption.IsAscending;  

         results = results.OrderBy( string.Format( "{0}{1}", propertyName, !isAscending ? " desc" : null ) );
    }  

    return results;  
}
0 голосов
/ 03 мая 2010

Хотя я думаю, что решение @ tvanfosson будет отлично работать, я также изучаю эту возможность:

    /// <summary>
    /// This extension method is used to help us apply ISortOptions to an IQueryable.
    /// </summary>
    /// <param name="collection">This is the IQueryable you wish to apply the ISortOptions to.</param>
    /// <param name="sortOptions">These are the ISortOptions you wish to have applied. You must specify at least one ISortOption (otherwise, don't call this method).</param>
    /// <returns>This returns an IQueryable object.</returns>
    /// <remarks>This extension method should honor deferred execution on the IQueryable that is passed in.</remarks>
    public static IOrderedQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
    {
        Debug.Assert(sortOptions != null, "ApplySortOptions cannot accept a null sortOptions input.");
        Debug.Assert(sortOptions.Count() > 0, "At least one sort order must be specified to ApplySortOptions' sortOptions input.");

        var firstSortOption = sortOptions.OrderBy(o => o.Priority).First();
        var propertyName = firstSortOption.Property.MemberWithoutInstance();
        var isAscending = firstSortOption.IsAscending;

        // Perform the first sort action
        var results = isAscending ? collection.OrderBy(propertyName) : collection.OrderByDescending(propertyName);

        // Loop through all of the rest ISortOptions
        foreach (var sortOption in sortOptions.OrderBy(o => o.Priority).Skip(1))
        {
            // Make a copy of this or our deferred execution will bite us later.
            var currentOption = sortOption;

            propertyName = currentOption.Property.MemberWithoutInstance();
            isAscending = currentOption.IsAscending;

            // Perform the additional orderings.
            results = isAscending ? results.ThenBy(propertyName) : results.ThenByDescending(propertyName);
        }

        return results;
    }

используя код из этого вопроса ответа :

    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder(source, property, "OrderBy");
    }

    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder(source, property, "OrderByDescending");
    }

    public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder(source, property, "ThenBy");
    }

    public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder(source, property, "ThenByDescending");
    }

    private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
    {
        var props = property.Split('.');
        var type = typeof (T);
        var arg = Expression.Parameter(type, "x");
        Expression expr = arg;
        foreach (var prop in props)
        {
            // use reflection (not ComponentModel) to mirror LINQ
            var pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
        var lambda = Expression.Lambda(delegateType, expr, arg);

        var result = typeof (Queryable).GetMethods().Single(
            method => method.Name == methodName
                      && method.IsGenericMethodDefinition
                      && method.GetGenericArguments().Length == 2
                      && method.GetParameters().Length == 2)
            .MakeGenericMethod(typeof (T), type)
            .Invoke(null, new object[] {source, lambda});
        return (IOrderedQueryable<T>) result;
    }
...