MVC DI/IoC слишком много зависимостей?

Учитывая этот пример контроллера MVC; psuedo code...

PostController
{
 private IGetPost _getPost;
 private ICreatePost _createPost;
 private IDeletePost _deletePost;
 private IUpdatePost _updatePost;
 public ActionResult Get()
 {
 return _getPost();
 }
 public ActionResult Create(post)
 {
 return _createPost(post);
 }
 public ActionResult Update(posts)
 {
 return _updatePost(post);
 }
 public ActionResult Delete(post)
 {
 return _deletePost(post);
 }
}

Вопрос, который у меня есть, заключается в том, что, если вызывается какое-либо одно из действий над контроллером, все зависимости контроллера имеют для них созданные экземпляры, которые выглядят как buzzkill производительности. Есть лучший способ сделать это? Единственное, что у меня было, это создать 4 разных контроллера, каждый из которых имеет только одно действие, но это похоже на перебор. Я также подумал о том, чтобы вызвать DependencyResolver непосредственно в каждом действии, но я не уверен, что последствия для тестируемой единицы будут, если я это сделаю.

2 ответа

Почему это звучит buzzkill? Вы оценили это так? Хотя накладные расходы при определении того, какие реализации сопоставляются с интерфейсами, большинство контейнеров зависимостей (DI) контейнеров, достойных их соли, сделают это сопоставление один раз (и обычно создают скомпилированный код "на лету" , чтобы поиск был как можно быстрее) для создания сопоставлений.

Тогда это просто вопрос создания объектов, которые не будут отличаться, если вы должны использовать ключевое слово new.

В общих случаях это не будет хитом производительности. Учитывая это веб-приложение, если вы получаете уровни, то это очень хорошо может помешать масштабированию; каждая из этих операций является дешевой, но при умножении на миллионы в совокупности она очень дорогая, и обычно это типы вещей, которые могут привести к разрешению ресурсов и т.д.

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

Если реализация этих четырех интерфейсов (или любого их количества) является дорогостоящей, это не функция DI, это функция вашего кода и что вы получите больше преимуществ от оптимизации.

Единственное место, где было бы полезно настроить инжекцию зависимостей, было бы, если бы конструкция одной или нескольких из этих реализаций имела высокие накладные расходы, и у вас был контейнер DI для одного экземпляра для всех реализаций интерфейса вместо одного экземпляра для реализации интерфейса. Тем не менее, вы должны смотреть только на управление жизненным циклом через уровень DI, когда вы определили, что этот параметр доступен для вас (что означает, что один экземпляр этого класса для обслуживания всех запросов является жизнеспособным, он поточно-безопасен? любые ресурсы в течение очень долгого времени? и т.д.)

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

Однако, глядя на действия, которые у вас есть, кажется, что логический разрыв, который у вас есть, правильный.

Короче говоря, не пытайтесь оптимизировать производительность в тех местах, которые вы не измеряли, чтобы быть фактором.

Тем не менее, независимо от того, что вы делаете, не разрешайте зависимости внутри вашего класса. Если вы это сделаете, вы потеряете все преимущества DI и плотно привязываете свой класс к контейнеру DI (и, возможно, убиваете тестируемость в то время как на нем).


Я не уверен, что производительность действительно будет проблемой, если у вас действительно нелепое количество зависимостей. В любом случае, это хорошее упражнение, чтобы посмотреть на вашу архитектуру.

То, как мы это делаем, - это абстрагировать вызов уровня сервиса внешнему классу, называемому обработчиком. Это из-за вдохновения Project Silk. Таким образом, вы не только уменьшаете свои контроллеры и делаете их гораздо более управляемыми, но избегаете такого роста зависимости.

В этом случае у вас будет один контроллер, который для каждого действия просто решает обработчик и его зависимости и выполняет его. Обработчики определяются как малые классы с очень небольшим (обычным) методом.

class MyController : BaseController
{
 public ActionResult Get(int id)
 {
 return Using<getitemhandler>().Execute(id);
 }
}
class GetItemHandler
{
 private IDependency _myDep;
 public GetItemHandler(IDependency dep)
 {
 _myDep = dep;
 }
 public Execute(int id)
 {
 return _myDep.Get(id);
 }
}
</getitemhandler>

Обратите внимание, что способ, которым это работает, заключается в том, что метод Using<t>()</t> (определенный в BaseController) использует контейнер IoC для разрешения обработчика, таким образом захватывая все его зависимости. Тогда мы можем просто использовать его как обычно. Я думаю, что этот шаблон действительно помогает отделить обязанности и держать ваши классы и контроллеры приятными и худшими.

licensed under cc by-sa 3.0 with attribution.