Внедрение конструктора в базовый класс с использованием автофака - PullRequest
26 голосов
/ 26 августа 2011

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

public abstract class BaseController : Controller
{
    protected ILogger { get; private set; }

    protected BaseController()
    {
    }

    protected BaseController(ILogger logger)
    {
        Logger = logger;
    }
}

Это не работает, когда я извлекаю из него контроллер.

Я могу заставить это работать, только когда я явно вызываю конструктор явно из контроллера.Это правильный способ сделать это?

public class PublicController : BaseController
{
    public PublicController()
    {
    }

    public PublicController(ILogger logger) : base(logger)
    {

    }
}

Кроме того, при использовании сборки интеграции mvc, похоже, нет способа совместно использовать контейнер для других классов, чтобы они могли выполнять свое собственное разрешение.Я где-то читал, что это не поощряется, почему бы и нет?Это только для того, чтобы отделить зависимость какой-либо одной структуры IOC?Является ли инъекция конструктора только способом заполнения зависимостей по иерархии.

Спасибо

Ответы [ 3 ]

55 голосов
/ 26 августа 2011

Вызов конструктора базового класса в явном виде - единственный способ сделать это, используя внедрение конструктора в C #. Похоже, вы должны удалить конструкторы без параметров из BaseController и PublicController, так как они никогда не должны вызываться, когда доступен регистратор.

Проблема внедрения зависимостей в базовый контроллер является обычной для ASP.NET MVC и IoC. Есть несколько вариантов / школ мысли.

1.) Используйте совокупные услуги. Чтобы сохранить конструкторы производных классов простыми, создайте единый сервис, который предоставляет или делегирует все различные сервисы, необходимые базовому контроллеру (например, IBaseControllerDependencies или аналогичный). Затем передайте этот единственный сервис BaseController, как вы это делаете с ILogger здесь.

Существуют различные плюсы и минусы в зависимости от вашего приложения и количества используемых вами базовых классов. Чтобы узнать больше об этом, обратитесь к Google за «Агрегатными услугами Autofac».

2.) Используйте свойство инъекции. Сделайте свойство ILogger вашего базового класса общедоступным и настройте контейнер, используя:

builder.RegisterControllers().PropertiesAutowired();

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

3.) Реорганизовать функциональность базового контроллера в различные фильтры действий. Autofac может внедрить фильтры действий в конвейер вызова действий MVC. Таким образом, фильтры могут принимать зависимости, которые были от базового класса, и те же проблемы могут применяться сквозным образом. Подробнее об этом в Интернете, ExtensibleActionInvoker и .InjectActionInvoker() указывают на информацию, которая вам понадобится. Не всегда возможно со всеми проблемами.

4, также ответ на ваш второй вопрос.) Разрешите зависимости базового контроллера, используя расположение службы из DependencyResolver.Current.

var logger = DependencyResolver.Current.GetService<ILogger>();

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

Надеюсь, это поможет, немного мозгов, я знаю :) Другие, возможно, могут добавить еще несколько идей к ним.

2 голосов
/ 26 августа 2011

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

Что касаетсяВаши дополнительные вопросы: у класса не обязательно есть способ сделать свое собственное разрешение, но класс может получить зависимость от автоматически разрешаемой фабрики объектов для известного типа (чего чаще всего достаточно).Если вы зарегистрировали A, вы можете получить зависимость от Func<A>.В Autofac есть много таких типов отношений;см. http://code.google.com/p/autofac/wiki/RelationshipTypes.

"Общий доступ к контейнеру" не рекомендуется, потому что он имеет тенденцию скрывать зависимости класса.Если зависимость от контейнера берется или упоминается как глобальный объект «IoC», выраженность определенных зависимостей теряется.

Разрешение зависимостей по иерархии не должно быть проблемой, так как вы ужеиспользуя c'or инъекцию, я не уверен, какая у вас проблема.Есть дополнительные проблемы, такие как изменение всех ваших подклассов, если ваши зависимости базового класса изменяются.К счастью, у Autofac есть ответ на этот вопрос в Aggregate Services (http://code.google.com/p/autofac/wiki/AggregateService).

Autofac - отличный, глубокий инструмент. Еще одно предложение, если вы только начинаете, пожалуйста, уделите некоторое время, чтобы обдумать время жизни объекта и области его жизни(особенно http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/). Удачи!

1 голос
/ 02 ноября 2017

Autofac для .NET Core

Установка

PM> Install-Package Autofac.Core.NonPublicProperty

Использование

builder.RegisterType<AppService>()
  .AsImplementedInterfaces()
  .AutoWireNonPublicProperties();
...