Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
GetService<IQuery<IQueryable<DisconnectedAppDomain>, DisconnectedAppFilter>>();
В идеале контроллеры должны быть слишком тупы, чтобы их покрывать тестами, нам это никогда не понадобится, если и покрывать их, то интеграционными тестами
Constructor injection в контроллерах банально неудобен
конструктор разрастается при и при 5 и больше зависимостях выглядит тяжеловесно
К тому же переданные в конструктор параметры нужно инкапсулировать в классе, что добавляет телодвижений.
Где у вас очевидность зависимостей то?
Какие проблемы с этим кодом я вижу:
На мой взгляд Constructor injection вполне себе удобен, но в качестве контраргумента довелось слышать, что конструктор разрастается при и при 5 и больше зависимостях выглядит тяжеловесно. К тому же переданные в конструктор параметры нужно инкапсулировать в классе, что добавляет телодвижений.
Каждый конкретный обработчик конструктора может иметь отличные от других обработчиков зависимости.
Ну, подходы же можно и объединить: некоторые зависимости — в конструктор, а некоторые — в action.
Разделять данные и сервисы можно атрибутами.
Или просто по наличию сервиса в контейнере.
По поводу же разных методологий в разных частях системы — что именно в этом неудобного? Если, конечно, таких методологий 2-3, а не 10-20.
Наверное, вы имели в виду, обработчик контроллера, проще говоря — action?
Во-первых, часто ли вам надо иметь у них разные зависимости? А если у них у всех есть одна и та же зависимость, вы получите неслабое дублирование кода
Во-вторых, обычно параметрами action являются данные, пришедшие в запросе — как вы планируете их разделять?
public function editAction($require, $urlParams){
...
}
Ну и в-четвертых, неудобно иметь разные DI-методологии в разных частях системы
А нету чего-то типа this.Request или this.Request.GetRouteData()?
<?php
class UserController{
private $session;
public function __construct($session){
$this->session = $session;
}
public function register($require, $userTable){
$login = $require->param('login');
$pass = $require->param('pass');
...
$id = $userTable->insert(['login' => $login, 'password' => $pass]);
$this->session->save(['id' => $id, 'login' => $login]);
}
public function logout(){
$this->session->delete();
}
}
<?php
$manager = new Di\Manager;
$manager->set('require', $require);
$manager->set('session', $session);
$manager->set('userTable', function(){
...
return $userTable;
});
// Инстанциация контроллера через разрешение зависимостей конструктора
$controller = $manager->constructInjection($controllerName);
// Вызов экшена контроллера с разрешением его зависимостей
$result = $manager->methodInjection($controller, $action);
$response->body($result);
$response->send();
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser {UserName = model.Email, Email = model.Email};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
return View(model);
}
По мне так логичней, что класс, выполняющий набор действий, заранее сообщает о своих зависимостях
class MyClass{
...
public function __construct($x, $y, $z, $a, $b, $c, ...){
...
}
public function methodA(){}
public function methodB(){}
public function methodC(){}
}
да, мне частенько нужны различные зависимости в разных методах контроллера.
Как минимум очень часто в методах используются разные Mapper'ы.
части URL выделенные роутером хранятся в зависимости $urlParams, а параметры запроса, переданные клиентом, в $require.
public async Task Put(string id, Location data)
{...}
И то, и другое хранится в локаторе, что позволяет разрешить их с помощью DI.
Почему же?
Ыыы, а у нас просто один маппер позволяет мапить все объекты
Это все прекрасно, но где зависимости?
Прекрасно — это вот так
Но зачем? И, главное, по каким признакам?
Униформность нарушается
Один контроллер на все запросы не используете? )
Внутри метода контроллера он выделяет нужные ему параметры и валидирует их.
Сервисы должны хранится в локаторе.
Тобишь если у нас есть одно рабочее решение, его нужно превращать в золотой молоток?
Это лишний повторяющийся код
Вот только пришедшие снаружи данные — это не сервисы. Что им делать в локаторе?
Если у нас есть рабочее решение, то не надо рядом внедрять почти такое же, но другое. Это будет путать программиста
Который можно легко унифицировать и превратить в не повторящийся, что и было сделано с помощью аннотаций.
Пришедшие снаружи данные можно определить как пакет, который вполне можно поместить в локатор для доступа к ним из любой части обработчика.
Превращать все частные зависимости методов в зависимости объекта, это золотой молоток.
Значит, это происходит не внутри метода контроллера
Зачем? Это, по факту, глобальное состояние
Сегодня они одни, а завтра — другие; сегодня они используются в одном методе, завтра — во всех. В то же время, параметры его методов — это его внешний контракт, чем меньше он меняется, тем лучше
Чтобы можно было разрешить их как зависимость. Очевидно же.
А семантика контроллера, это не контракт?
А зачем вам разрешать состояние как зависимость?
Нет
Потому что это не состояние, а пакет данных пользователя, от которого зависят некоторые методы моего контроллера.
а в изменении семантики других методов видите?
Методы контроллера не могут зависеть от пакета данных пользователя, пакет данных — это входные данные, аргументы
Так в том-то и дело, что зависимости никак не определяют семантику метода
Мысленно назовите всю совокупность этих данных пакетом и попробуйте сделать так, чтобы метод от них «не зависел».
Еще как определяют.
PutArticle — в том, чтобы сохранить статью (в некое хранилище). То, что при этом ему нужен сервис безопасности и (особенно) сервис логирования — это не его семантика.«Зависимость» метода (уже в программном смысле) — это какой-то объект, нужный ему для корректного выполнения задачи
То, что при этом ему нужен сервис безопасности и (особенно) сервис логирования — это не его семантика
Почему именно объект?
Если я обмениваюсь данными с сервером в виде JSON, то это уже зависимости?
Откуда же метод получит сервис логирования?
Потому что обычно внутрение сущности программы в ООП — это объекты
Нет, это входные (или выходные) данные
А это не имеет никакого значения для его семантики. Более того, его семантика не изменится от того, использует он метод логирования или нет
зависимости декларируются в конструкторе (обычно)
наследуется базовый контроллер, который может дать ограничение на конструктор.
Соотвествено реализация IDependencyResolver и является адаптером, который позволяет интегрировать оба фреймворка.
IDependencyResolver (за компанию с еще некоторыми классами) — это точка расширения, позволяющая использовать в WebAPI тот DI-контейнер, который нужен программисту. Совершенно штатная.Неа. IDependencyResolver (за компанию с еще некоторыми классами) — это точка расширения, позволяющая использовать в WebAPI тот DI-контейнер, который нужен программисту. Совершенно штатная.
app.ApplicationServices. Всей-то разницы.internal static T GetService<T>(ApiController controller)
{
return (T) controller.Configuration.DependencyResolver.GetService(typeof (T));
}
Неа. IDependencyResolver (за компанию с еще некоторыми классами) — это точка расширения, позволяющая использовать в WebAPI тот DI-контейнер, который нужен программисту. Совершенно штатная.
public class HomeController : Controller
{
[Dependency]
public IService1 Service1 { get; set; }
[Dependency]
public IService2 Service2 { get; set; }
public HomeController(IUnityContainer uc)
{
uc.BuildUp(this);
}
public ActionResult Index()
{
var data = Service1.Load();
// ...
}
}Но я ещё не встречал проекта, где на полпути меняли бы БД, ORM или контейнер.
new HomeController(uc). Если бы создание контроллера было в моём коде, я бы написал через Resolve, но поскольку ASP.NET MVC делает constructor injection, тут такой костыль.поскольку ASP.NET MVC делает constructor injection, тут такой костыль.
В идеале контроллеры должны быть слишком тупы, чтобы их покрывать тестами, нам это никогда не понадобится, если и покрывать их, то интеграционными тестами
Что это за опциональные зависимости такие? Типа есть работаю, а нет начинаю менять поведение?
Любовь или брак по расчету с Dependency Injection?