C #, LINQ универсальный метод сортировки для сортировки списка по свойствам объекта и вложенным свойствам - PullRequest
0 голосов
/ 23 мая 2019

В моей модели EF есть объект с именем User:

public class User
{
    public int UserId { get; set; }
    public Branch HomeLocation{ get; set; }   
    public string CellPhone { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; } 
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserCode { get; set; }
}

Ветвь - это другая сущность в модели:

public class Branch
{    
    public int BranchId { get; set; }
    public string BranchName{ get; set; }
    public string Address { get; set; } 
}

Мое требование - получить список пользователей и отобразить его в сетке, а затем отсортировать список по некоторым столбцам (по одному за раз). Скажем, например, сортировка по имени пользователя, имени, фамилии и HomeLocation. При сортировке по месту расположения она должна быть отсортирована по имени филиала.

У меня есть много таких таблиц, которые отображают и другие данные. Поэтому я хочу разработать общий механизм сортировки, и я добился его, используя некоторые примеры, найденные в Google, например этот :

public class GenericSorter<T>
{
    public IEnumerable<T> Sort(IEnumerable<T> source, string sortBy, string sortDirection)
    {
        var param = Expression.Parameter(typeof(T), "item");

        var sortExpression = Expression.Lambda<Func<T, object>>
            (Expression.Convert(Expression.Property(param, sortBy), typeof(object)), param);

        switch (sortDirection.ToLower())
        {
            case "asc":
                return source.AsQueryable<T>().OrderBy<T, object>(sortExpression);
            default:
                return source.AsQueryable<T>().OrderByDescending<T, object>(sortExpression);

        } 
    }
}

Однако сортировка по домашнему расположению не удалась, поскольку она должна быть отсортирована по внутреннему свойству объекта пользователя. Я тоже пытался использовать Dynamic LINQ library , но безуспешно.

Обновление: обратите внимание, что мне нужно отсортировать список, а не IQueryable, потому что мой список содержит поля, зашифрованные с помощью AE, которые не поддерживают сортировку на уровне базы данных.

Может ли кто-нибудь указать мне, как добиться динамической сортировки по внутреннему свойству?

Update2: я последовал примеру и реализовал сортировку, используя методы расширения, и вот как она применяется в моем списке:

var users = (from u in context.Users.Include("Branch")
                    where (u.FkBranchId == branchId || branchId == -1) && u.IsActive
                        && (searchTerm == string.Empty || (u.FirstName.Contains(searchTerm) || u.LastName.Equals(searchTerm)
                            || u.UserName.Contains(searchTerm) || u.UserCode.Contains(searchTerm)))
                    select u).ToList();

        var rowCount = users.Count;

        var orderedList = users.OrderBy(sortInfo.SortColumn).Skip(pageInfo.Skip).Take(pageInfo.PageSize).ToList();

Но я получаю следующую ошибку: Объект типа «System.Linq.Expressions.Expression 1[System.Func 2 [ClientData.User, System.String]]» не может быть преобразован в тип «System.Func`2 [ClientData.User, System.String] '.

Ошибка выдается из следующего:

object result = typeof(Enumerable).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 });

После этого я получаю следующую ошибку, в некоторых случаях, как описано в комментарии: enter image description here

1 Ответ

1 голос
/ 24 мая 2019

Адаптируйте код из @MarcGravell, найденный здесь .

public static class EnumerableExtensions
{
    public static IOrderedEnumerable<T> OrderBy<T>(
        this IEnumerable<T> source,
        string property)
    {
        return ApplyOrder<T>(source, property, "OrderBy");
    }

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

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

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

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

        object result = typeof(Enumerable).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.Compile() });
        return (IOrderedEnumerable<T>)result;
    }
}

ОБНОВЛЕН *

Используйте его из списка <>:

var list = new List<MyModel>();
list = list.OrderBy("MyProperty");
...