Для контроллера не является плохой практикой прямой вызов хранилища. «Сервис» - это просто еще один инструмент, поэтому используйте его там, где это имеет смысл.
Николай Данте прокомментировал:
... Выберите правильный шаблон для правильного применения. Я бы сказал, что вы должны сделать вашу заявку последовательной.
Я не думаю, что последовательность является наиболее важным аспектом. Класс «service» предназначен для инкапсуляции некоторой логики более высокого уровня, поэтому контроллеру не нужно его реализовывать. Если для данной операции не требуется «логика более высокого уровня», просто перейдите непосредственно в хранилище.
Чтобы обеспечить хорошее разделение проблем и тестируемость, хранилище должно быть зависимостью, которую вы вводите в службу через конструктор:
IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);
service.DoSomething(...);
Если для поиска записей в базе данных требуется какой-то параметризованный запрос, класс обслуживания мог бы быть хорошим местом для использования в вашей модели представления и построения запроса, который затем выполняется хранилищем.
Аналогично, если у вас есть модель сложного представления для формы, класс обслуживания может инкапсулировать логику создания, обновления и удаления записей, вызывая методы в ваших моделях / сущностях домена, а затем сохранять их с помощью репозитория.
Если вы движетесь в противоположном направлении, если вашему контроллеру необходимо получить запись по его идентификатору, то делегирование объекту службы для этого похоже на попадание в чертёж кувалдой - это намного больше, чем вам нужно.
Я обнаружил, что контроллер находится в лучшем положении для обработки транзакции или объекта Unit Of Work . Контроллер или объект Unit Of Work будут затем делегировать объектам обслуживания для сложных операций или переходить непосредственно в хранилище для простых операций (таких как поиск записи по Id).
public class ShoppingCartsController : Controller
{
[HttpPost]
public ActionResult Edit(int id, ShoppingCartForm model)
{
// Controller initiates a database session and transaction
using (IStoreContext store = new StoreContext())
{
// Controller goes directly to a repository to find a record by Id
ShoppingCart cart = store.ShoppingCarts.Find(id);
// Controller creates the service, and passes the repository and/or
// the current transaction
ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);
if (cart == null)
return HttpNotFound();
if (ModelState.IsValid)
{
// Controller delegates to a service object to manipulate the
// Domain Model (ShoppingCart)
service.UpdateShoppingCart(model, cart);
// Controller decides to commit changes
store.SaveChanges();
return RedirectToAction("Index", "Home");
}
else
{
return View(model);
}
}
}
}
Я думаю, что набор услуг и работа с репозиториями напрямую вполне приемлемы. Вы можете дополнительно инкапсулировать транзакцию в объекте Unit Of Work, если вы чувствуете необходимость.
Распределение обязанностей выглядит следующим образом:
- Контроллер контролирует поток приложения
- Возвращает «404 Not Found», если корзина покупок отсутствует в базе данных
- Повторно отображает форму с сообщениями проверки, если проверка не удалась
- Сохраняет корзину, если все проверено
- Контроллер делегирует классу обслуживания для выполнения бизнес-логики в ваших моделях домена (или объектах). Сервисные объекты не должны реализовывать бизнес-логику! Они выполняют бизнес-логику.
- Контроллеры могут делегировать напрямую в репозитории для простых операций
- Сервисные объекты берут данные в модели представления и делегируют моделям домена для выполнения бизнес-логики (например, сервисный объект вызывает методы в моделях домена перед вызовом методов в хранилище)
- Сервисные объекты делегируют в репозитории для сохранения данных
- Контроллеры должны либо:
- Управлять временем жизни транзакции или
- Создайте объект Unit Of Work для управления временем жизни транзакции.