Я думаю, что ваш подход - неправильный способ использования 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();
И это ясно, кратко, расширяемо, компонуемо, многократно используется, и вы контролируете время выполнения .