Элегантное уменьшение количества зависимостей в контроллерах ASP.NET MVC - PullRequest
5 голосов
/ 17 марта 2009

Мы разрабатываем крупный проект ASP.NET MVC, и запах кода начинает поднимать голову.

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

Я изо всех сил пытаюсь придумать хороший способ уменьшить количество объектов, которые создаются без необходимости для 90% вызовов.

Вот несколько идей, с которыми я играюсь:

  1. Разделение контроллеров на более мелкие и более целевые.
    • В настоящее время у нас имеется примерно контроллер для каждой сущности домена, что привело к красивым URL-адресам, которые мы хотели бы эмулировать, а это означало бы, что мы получим гораздо более сложную схему маршрутизации.
  2. Передача интерфейса, обертывающего контейнер IoC.
    • Это будет означать, что объекты будут создаваться только тогда, когда они были явно необходимы. Тем не менее, это похоже на то, как наносить помаду на свинью.
  3. Расширение фреймворка для достижения какой-то безумной комбинации двух.

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

Ответы [ 4 ]

3 голосов
/ 17 марта 2009

Я размышлял над решением этой самой проблемы, и вот что я придумал:

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

Я буквально только что взбил это, так что это немного наивно и не проверено на гнев, но я собираюсь реализовать это как можно скорее, чтобы попробовать. Предложения приветствуются!

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

в global.asax:

protected void Application_Start()
{
    ControllerBuilder.Current.SetControllerFactory(
            new StructureMapControllerFactory());
}

вот структура mapcontrollerfactory:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            var controller = 
                    ObjectFactory.GetInstance(controllerType) as Controller;
            controller.ActionInvoker = 
                    new StructureMapControllerActionInvoker();
            return controller;
        }
        catch (StructureMapException)
        {
            System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
            throw;

        }
    }
}

и структура mapcontrolleractioninvoker (может быть более умным)

public class StructureMapControllerActionInvoker : ControllerActionInvoker
{
    protected override object GetParameterValue(
            ControllerContext controllerContext, 
            ParameterDescriptor parameterDescriptor)
    {
        object parameterValue;
        try
        {
            parameterValue = base.GetParameterValue(
                    controllerContext, parameterDescriptor);
        }
        catch (Exception e)
        {
            parameterValue = 
                    ObjectFactory.TryGetInstance(
                            parameterDescriptor.ParameterType);
            if (parameterValue == null)
                throw e;
        }
        return parameterValue;
    }
}
1 голос
/ 18 марта 2009

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

Использование ServiceLocation также поможет (и да, я, по сути, повторяю ответ Дениса Троллера - что, вероятно, не очень хорошо, но я действительно проголосовал за его ответ).

1 голос
/ 17 марта 2009

Я бы отдельно рассмотрел проблему зависимостей и создания зависимых объектов . Зависимость - это просто тот факт, что исходный код контроллера ссылается на определенный тип , Это связано со сложностью кода, но не требует затрат времени исполнения. С другой стороны, создание экземпляра объекта требует затрат времени выполнения.

Единственный способ уменьшить количество зависимостей кода - это сломать контроллеры. Как вы говорите, все остальное просто делает зависимости немного красивее. Но создание зависимостей (в отличие от создания экземпляров зависимых объектов, о которых я расскажу чуть позже) вполне может быть достаточным решением, которое вам не нужно разбивать на контроллеры. Так что IoC - достойное решение для этого, я думаю.

Re: создавая объекты, вы пишете: «... некоторые из этих зависимостей используются только для одного из методов действия на контроллере, но, очевидно, создаются для каждого экземпляра контроллера». Это кажется мне реальной проблемой, а не зависимостью per se . Потому что с расширением вашего проекта будет только хуже. Вы можете решить эту проблему, изменив создание экземпляров объектов, чтобы они не возникали, пока они не понадобятся. Одним из способов было бы использование свойств с отложенной реализацией. Другой способ - использовать аргументы ваших методов действий с помощью связывателей моделей, которые создают экземпляры нужных вам объектов. Еще один способ - написать функции, которые возвращают нужные вам экземпляры. Трудно сказать, какой путь лучше, не зная назначения объектов, которые вы используете.

1 голос
/ 17 марта 2009

Существует понятие «сервисный локатор», которое было добавлено в такие работы, как Prism. Преимущество заключается в уменьшении этих накладных расходов.

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

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

...