Резольвер зависимостей Castle Windsor для MVC 3

Поскольку реализация IoC / DI в MVC 3, скорее всего, находится в своей окончательной форме в RC, я ищу обновленную реализацию DependencyResolver, IControllerActivator и IViewPageActivator с использованием Caste Windsor. Есть ли какие-нибудь примеры, которые были обновлены для MVC 3 RC?

РЕДАКТИРОВАТЬ №1. Реализация преобразователя зависимостей Windsor действительно тривиальна, но чего-то еще не хватает. В отличие от примера Джеффа Путца Ninject (ниже), похоже, что это не так просто, как с Виндзором. После установки такого преобразователя зависимостей,

DependencyResolver.SetResolver(new WindsorDependencyResolver(container)); 

Виндзор выбрасывает исключение ComponentNotFoundException. Мне нужно предоставить реализации для IControllerFactory и IControllerActivator. Поскольку DefaultControllerFactory знает DependencyResolver, это можно решить следующим образом:

Component.For<IControllerFactory >().ImplementedBy<DefaultControllerFactory>()
Component.For<IControllerActivator >().ImplementedBy<WindsorControllerActivator>(),

WindsorControllerActivator тоже тривиален. Однако это приводит к другому исключению ComponentNotFoundException для IViewPageActivator.

Это заставляет меня думать, что я что-то упускаю. Это не может быть более сложным, чем реализация фабрики контроллеров и вызов ControllerBuilder.Current.SetControllerFactory в стиле MVC 2.0.

РЕДАКТИРОВАТЬ №2. Я упустил тонкую, но важную деталь: преобразователь зависимостей должен возвращать значение null, если служба не может быть найдена. Реализация следующая:

public class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.Resolve(serviceType) : null;
    }

  public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.ResolveAll(serviceType).Cast<object>() : new object[]{};
    }
}

ИЗМЕНИТЬ №3

Отвечая на вопрос в комментариях. Если вы обнаружите, что вам нужен собственный IControllerActivator, вот простая реализация для Windsor:

public class WindsorControllerActivator : IControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorControllerActivator(IWindsorContainer container)
    {
        this.container = container;
    }

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        return (IController)container.GetService(controllerType);
    }
}

}

Опять же, это НЕ необходимо, чтобы базовый DI работал с Windsor и преобразователем зависимостей MVC3.

РЕДАКТИРОВАТЬ №4. Основываясь на некоторых дальнейших исследованиях и отзывах, кажется, что реализация традиционной фабрики контроллеров является лучшим подходом для Windsor и MVC3. Проблема заключается в том, что в интерфейсе IDependencyResolver отсутствует метод выпуска, который может вызвать утечку памяти, если Windsor не удалит свои компоненты. Вероятно, это не будет проблемой, если все ваши зависимости разрешены с помощью жизненного цикла PerWebRequest, но все же лучше не рисковать. Вот базовая реализация фабрики контроллеров Windsor для MVC3.

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        this.container = container;
    }

    public override void ReleaseController(IController controller)
    {
        container.Kernel.ReleaseComponent(controller);
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerComponentName = controllerName + "Controller";
        return container.Kernel.Resolve<IController>(controllerComponentName);
    }
}

ИЗМЕНИТЬ №5. Если вы используете области MVC, описанная выше реализация вам не подойдет. Вам нужно будет зарегистрировать каждый контроллер на основе его полного имени и переопределить GetControllerInstance вместо CreateController:

 protected override IController GetControllerInstance(RequestContext context, Type controllerType)
    {
        if (controllerType != null)
        {
            return (IController)container.Kernel.Resolve(controllerType);
        }
        return null;
    }

person James H    schedule 10.11.2010    source источник
comment
MVC 3 уже исполнилось 15 часов, кто-нибудь напишет этот код!   -  person John Farrell    schedule 10.11.2010
comment
Да ... часть возврата null является ключевой, что не так очевидно в моем примере, если вы не знаете, что Ninject имеет этот метод TryGet и возвращает null, если не может найти совпадение. Я, наверное, мог бы быть более ясным. :)   -  person Jeff Putz    schedule 10.11.2010
comment
Есть шанс, что мы сможем увидеть больше этого кода. WindsorControllerActivator? где это, гугл ноль результатов.   -  person MvcCmsJon    schedule 19.11.2010
comment
Необязательно, чтобы это работало ... но в этом нет ничего особенного. WindsorControllerActivator реализует IControllerActivator, который имеет единственный метод, который возвращает IController.   -  person James H    schedule 20.11.2010
comment
Ах, спасибо, я поздно пришла в церковь Иока, но теперь я уступила и догоняю.   -  person MvcCmsJon    schedule 21.11.2010
comment
Этот активатор ошибочен, я пытался его использовать, но если вы зарегистрируете этот активатор в своем контейнере DI, DefaultControllerFactory будет использовать этот активатор. Когда вызывается create, если в контейнере нет контроллера, он не выдаст то же сообщение об ошибке, что видел автор. Чтобы решить эту проблему, вы можете проверить наличие нуля в контейнере, а затем добавить его, если оно равно нулю. if (controller == null) {container.Register (Component .For (controllerType) .LifeStyle.Singleton); controller = (IController) container.GetService (controllerType); }   -  person CrazyDart    schedule 29.12.2010
comment
И на основе всего этого я мог бы использовать Ninject   -  person Jon    schedule 20.01.2011


Ответы (3)


Интерфейс не изменился с момента выхода бета-версии, поэтому все реализации для различных фреймворков должны по-прежнему работать. И, по правде говоря, это не такой уж и сложный интерфейс ... вы сможете без особых хлопот создать свой собственный. Например, я сделал это для Ninject:

public class NinjectDependencyResolver : IDependencyResolver
{
    public NinjectDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    private readonly IKernel _kernel;

    public object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType);
    }
}

Затем подключите его к global.asax следующим образом:

    private static IKernel _kernel;
    public IKernel Kernel
    {
        get { return _kernel; }
    }

    public void Application_Start()
    {
        _kernel = new StandardKernel(new CoreInjectionModule());
        DependencyResolver.SetResolver(new NinjectDependencyResolver(Kernel));
        ...
    }

Помните, что в этот момент вы получаете все виды вкусностей бесплатно, включая DI для контроллеров, фабрики контроллеров, фильтры действий и базовые классы представлений.

РЕДАКТИРОВАТЬ: Чтобы было ясно, я не уверен, что это за «активаторы», но они, вероятно, вам не нужны. Интерфейс IDependencyResolver автоматически обрабатывает обновление контроллеров и представлений.

person Jeff Putz    schedule 10.11.2010
comment
Привет, почему вы храните ядро ​​в общедоступном статическом свойстве класса приложения? Я новичок в Ninject, но мне нужно только это: var resolver = new StandardKernel(new MyNinjectModule()); DependencyResolver.SetResolver(new NinjectDependencyResolver(resolver)); Спасибо! - person Pete Montgomery; 28.01.2011

У интерфейса MVC3 IDependencyResolver есть большая проблема: нет метода выпуска. Это означает, что существует потенциальная утечка памяти, если вы собираетесь использовать ее с Windsor. См. Мое сообщение в блоге об этом здесь:

http://mikehadlow.blogspot.com/2011/02/mvc-30-idependencyresolver-interface-is.html

person Mike Hadlow    schedule 03.02.2011

MVCContrib в настоящее время является авторитетным источником интеграции IoC-MVC. В настоящее время ветвь MVC3 включает только фабрику контроллеров и реализации IDependencyResolver (и пару других вещей). Я рекомендую создать ответвление репозитория и реализовать недостающие точки расширения (не должно быть слишком сложно), а затем отправить команде запрос на перенос.

person Mauricio Scheffer    schedule 10.11.2010
comment
Больше никогда! Для всех, кто сейчас это читает: mvccontrib.codeplex.com/workitem/7122 - person Chris Haines; 01.06.2012