Является ли хорошей практикой иметь запрос linq в контроллерах?

Я не очень хорошо знаком с шаблоном MVC. Не могли бы вы сказать мне, какой из следующих трех действий контроллера лучше? Спасибо:)

(1) Запрос в действии:

public ActionResult List()
{
   var query = repository.Query().Where(it => it.IsHandled).OrderBy(it => it.Id);
   // ...
}

(2) Запрос на обслуживание:

public ActionResult List() 
{
    var items = service.GetHandledItemsOrderById();
    // ...
}

(3) Имейте порядок в действии:

public ActionResult List()
{
    var items = service.GetHandledItems().OrderBy(it => it.Id);
    // ...
}

Если мы выберем (1), то у нас слишком много бизнес-логики в контроллере?

Если мы выберем (2), может быть много методов обслуживания, таких как GetXXXByYYY().

Если мы выберем (3), почему мы инкапсулируем Where(it => it.IsHandled), но не OrderBy(it => it.Id.

Любые идеи?

Ответ 1

Это зависит.:)

Мое мнение:

Мне нравится держать мою службу свободной, чтобы свести к минимуму дубликат кода. Я также являюсь поклонником труб и фильтров.

Вот что я сделал бы (и делаю так).

Сервис

public ICollection<Item> GetHandledItems<TKey>(OrderingOptions<Item, TKey> orderingOptions) 
{
   return repository
      .Query()
      .WhereHandled()
      .WithOrdering(orderingOptions)
      .ToList();     
}

ItemFilters.cs

public static IQueryable<Item> WhereHandled(this IQueryable<Item> source)
{
   return source.Where(it => it.IsHandled);
}

public static IOrderedQueryable<T> WithOrdering<T, TKey>(
   this IQueryable<T> source,
   OrderingOptions<T, TKey> orderingOptions)
{
   return orderingOptions.SortDescending 
      ? source.OrderByDescending(orderingOptions.OrderingKey) :                                                    
        source.OrderBy(orderingOptions.OrderingKey);
}

OrderingOptions.cs

 public class OrderingOptions<T,TKey>
 {
    public OrderingOptions(Expression<Func<T,TKey>> orderingKey, bool sortDescending = false)
    {
       OrderingKey = orderingKey;
       SortDescending = sortDescending;
    }

    public Expression<Func<T,TKey>> OrderingKey { get; private set; }
    public bool SortDescending { get; private set; }
 }

Таким образом, вы можете указать порядок в контроллере:

var items = service.GetHandledItems(new OrderingOptions(it => it.Id));

Различия между приведенными выше и опциями 3:

  • Выше материализуется последовательность перед возвратом в контроллер. Вариант 3 не является опасным (вы можете завершить запрос для просмотра и разлома шаблона MVC).
  • Общий "Заказ" POCO может использоваться в любом месте и хранит ваши запросы D-R-Y.
  • Сервис становится немой, и просто является помощником между репозиторием и контроллером (что все, что он должен делать, IMO). Логика (например, фильтры) абстрагирована в одном месте.

НТН

Ответ 2

Я уверен, что мнения могут отличаться, но я научился стараться хранить как можно больше бизнес-логики в сервисе. 3 был бы моим выбором. С 1 вы уже заметили проблему. С 2, вы представляете приоритет отображения в службе. С 3, вы, в случае необходимости, обрабатываете настройки отображения. Если вы должны ввести другой интерфейс для своего бизнес-уровня, вам потребуются потенциально ненужные итерации кода, выбирая 2.