Включить делегат OrderBy в параметры метода - PullRequest
7 голосов
/ 20 сентября 2011

У меня есть метод;

    public List<Task> GetTasksByAssignedTo(Guid contactId)
    {
        List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId).ToList();
        return tasks;
    }

, который возвращает список элементов.Скажем, теперь я хотел указать порядок сортировки, в котором я хочу вернуть список.

Так что я мог бы сортировать по Имени, Дату, Завершено и т. Д. И т. Д.

Как я могу включить это вметод в качестве параметра?Я не хочу использовать оператор switch, я бы хотел использовать лямбду, если это возможно.

Итак;

List<Task> list = GetTasksByAssignedTo("guid", ??????);

Или это неправильный подход.

Ответы [ 4 ]

10 голосов
/ 20 сентября 2011

Я думаю, что ваш подход - неправильный способ использования LINQ .

LINQ использует модель отложенного выполнения по причине. Он позволяет объединить последовательность операций, которые выполняются только тогда, когда вы указываете ему вычислять результат - часто с .ToList(), .ToArray(), .First() - но вы также можете форсировать вычисления, фильтруя с помощью OrderBy Предложение, которое использует Func<T, ?> в качестве параметра.

Теперь вы возвращаете List<Task>, что означает, что вы форсировали выполнение - что правильно делать, когда вы готовы использовать результаты, - но если вы затем продолжите выполнять дальнейшие операции, вы потенциально загружают в память гораздо больше записей, чем нужно.

Конечно, вы могли бы сделать это:

public List<Task> GetTasksByAssignedTo<P>(Guid contactId, Func<Task, P> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy) // this forces evaluation - sort happens in memory
        .ToList();
}

Чтобы выполнить выполнение в базе данных, вам нужно изменить его следующим образом:

public List<Task> GetTasksByAssignedTo<P>(
    Guid contactId,
    Expression<Func<Task, P>> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy)
        .ToList(); // Now execution happens here
}

Но проблема в том, что если вы сделали это:

var query =
    from t1 in GetTasksByAssignedTo(contactId, t => t.Name)
    join t2 in GetTasksByAssignedTo(contactId, t => t.Name)
        on t1.Name equals t2.Name
    select new { t1, t2 };

Поскольку ваш GetTasksByAssignedTo вносит записи в память, вы выполняете объединение в памяти. (Да, запрос немного надуманный, но принцип твердый.)

Часто гораздо лучше сделать это в базе данных.

Вот как это исправить:

public IQueryable<Task> GetTasksByAssignedTo<P>(
    Guid contactId,
    Expression<Func<Task, P>> orderBy)
{
    return dc.Tasks
        .Where(x => x.ContactId == contactId)
        .OrderBy(orderBy);
}

Теперь приведенный выше запрос не будет выполняться, пока вы не выполните query.ToList(), и все будет происходить в базе данных.

Но у меня есть еще большая проблема.

Вы скрываете много информации в GetTasksByAssignedTo. Кто-то, использующий код, не знает, что на самом деле он получает список, когда читает код, и он действительно не знает, правильно ли работает реальная реализация. Я думаю, что для такого рода запросов часто лучше оставить его простым LINQ.

Сравните это:

var tasks1 = GetTasksByAssignedTo(contactId);
var tasks2 = GetTasksByAssignedTo(contactId, t => t.Name);
var tasks3 = GetTasksByAssignedToDescending(contactId, t => t.Name);

var tasks4 = (
        from t in dc.Tasks
        where t.ContactId == contactId
        orderby t.Name descending
        select t
    ).ToList();

Первый запрос, tasks1 не так уж плох, но он не говорит вам, какой тип возврата;

Второй запрос, tasks2 делает что-то с t и свойством Name, но не говорит вам, что.

Третий запрос, tasks3, подсказывает, что сортировка происходит по убыванию, но не сообщает, является ли он загадочным свойством Name или чем-то еще.

Четвертый запрос tasks4 сообщает вам все, что вам нужно знать - это фильтрация задач по ContactId, обратный порядок результатов по Name и, наконец, возврат списка.

Теперь взгляните на этот запрос:

var query2 =
    from t1 in dc.Tasks
    where t1.ContactId == contactId
    join t2 in dc.Tasks on t1.Name equals t2.Name
    where t2.ContactId != contactId
    orderby t2.Name descending
    select t2;

Я могу прочитать это довольно легко и посмотреть, что он делает. Только представьте, какое имя будет у вспомогательного метода для этого! Или какое безумное вложение вспомогательных методов потребовалось бы.

Суть в том, что LINQ - это API для запросов.

Если вы отчаянно хотите создать вспомогательные методы, используйте методы расширения .

public static class TaskEx
{
    public static IQueryable<Task> WhereAssignedTo(this IQueryable<Task> tasks,
        Guid contactId)
    {
        return tasks.Where(t => t.ContactId == contactId);
    }

    public static IQueryable<Task> OrderByName(this IQueryable<Task> tasks)
    {
        return tasks.OrderBy(t => t.Name);
    }
}

Затем вы можете написать:

var tasks = dc.Tasks
    .WhereAssignedTo(contactId)
    .OrderByName()
    .ToList();

И это ясно, кратко, расширяемо, компонуемо, многократно используется, и вы контролируете время выполнения .

6 голосов
/ 20 сентября 2011

Вы можете передать Func<Task, object> в ваш метод для заказа:

public List<Task> GetTasksByAssignedTo(Guid contactId, Func<Task, object> someOrder)
{
    List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId)
                               .OrderBy(someOrder)
                               .ToList();
    return tasks;
}

Теперь вы можете назвать свой метод как

Func<Task, object> someOrder = (Task t) => t.DueDate;
List<Task> list = GetTasksByAssignedTo(someGuid, someOrder);

В целом я согласен с комментариями, хотя - этоне похоже, что порядок требуется для метода с именем GetTasksByAssignedTo.

2 голосов
/ 20 сентября 2011

@ BrokenGlass опередил меня.

Другой вариант - использовать метод расширения, который скрывает переключатель и представляет различные варианты упорядочения в качестве перечисления.

public static IEnumerable<Task> WithOrdering(this IEnumerable<Task> source, TaskOrdering order)
{
   switch (order)
   {
      case TaskOrdering.Name:
         return source.OrderBy(task => task.Name);
      case TaskOrdering.DueDate:
         return source.OrderByDescending(task => task.DueDate);       
   }
}

А потом:

public List<Task> GetTasksByAssignedTo(Guid contactId, TaskOrdering order)
{
    List<Task> tasks = dc.Tasks.Where(x => x.ContactId == contactId)
                               .WithOrdering(order)
                               .ToList();
    return tasks;
}

Я делаю это все время.Допуск предиката в качестве параметра метода может быть сложным, потому что, что произойдет, если вы захотите сделать восходящий / нисходящий?Для этого вам понадобится еще один параметр (bool), затем выполните проверку if / else, чтобы сделать OrderBy или OrderByDescending.

Скрыть логику в фильтре, и вы можете использовать ее где угоднов вашем приложении.

0 голосов
/ 19 августа 2016

Попробуйте это ... входные параметры в строке.Это соответствующее модифицированное решение от StackOverflow

    /// <summary>
    /// Sort List<typeparam name="T"></typeparam> objects base on string options
    /// </summary>      
    /// <param name="SortDirection">Ascending or Descending</param>   
    /// <param name="ColumnName">Column name in complex object (object.ColumnName)</param> 
 public static class ListExtension
{
    public static List<T> SortList<T>(this List<T> data, string sortDirection, string sortExpression)
    {
        try
        {
            switch (sortDirection)
            {
                case "Ascending":
                    data = (from n in data
                            orderby GetDynamicSortProperty(n, sortExpression) ascending
                            select n).ToList();
                    break;

                case "Descending":
                    data = (from n in data
                            orderby GetDynamicSortProperty(n, sortExpression) descending
                            select n).ToList();
                    break;

                default:
                    data = null; //NUL IF IS NO OPTION FOUND (Ascending or Descending)
                    break;

            }
            return data;
        } catch(Exception ex){
            throw new Exception("Unable to sort data", ex);
        }
    }

    private static object GetDynamicSortProperty(object item, string propName)
    {
        //Use reflection to get order type          
        return item.GetType().GetProperty(propName).GetValue(item, null);
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...