Использование контейнеров IoC; конкретно Виндзор - PullRequest
28 голосов
/ 15 декабря 2008

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

Я читал в контейнеры IoC (в данном случае Windsor), и мне не хватает того, как вы говорите с контейнером из различных частей вашего кода.

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

Создаю ли я единый глобальный страховой агент, который я передаю? Конечно, нет!

Я знаю, что должен назвать это:

WindsorContainer container = new WindsorContainer(new XmlInterpreter());

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

Неспособность понять это мешает мне понять, как работают жизненные циклы, и продолжать использовать некоторую удивительность IoC

Спасибо

Andrew

Ответы [ 5 ]

24 голосов
/ 15 декабря 2008

99% случаев это один экземпляр контейнера на приложение. Обычно вы инициализируете его в Application_Start (для веб-приложения), , например .

После этого все зависит от потребителя контейнера. Например, некоторые фреймворки, такие как Monorail и ASP.NET MVC , позволяют перехватывать создание экземпляров (в данном случае контроллеров), поэтому вы просто регистрируете контроллеры и их зависимости в контейнере и все, каждый раз, когда вы получаете запрос, контейнер заботится о внедрении каждого контроллера со своими зависимостями. См. Например этот контроллер ASP.NET MVC . В этих рамках вам вряд ли когда-либо понадобится вызывать или даже ссылаться на контейнер в ваших классах, что является рекомендуемым использованием.

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

Надеюсь, это очистит ваши сомнения.

3 голосов
/ 23 декабря 2008

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

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

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

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

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

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

Не уверен, как другие фреймворки поддерживают этот сценарий (Виндзор добавит IKernel).

1 голос
/ 15 декабря 2008

Я использую этот пример из блога Майкла Пулейо об использовании HttpModule для обработки моих зависимостей с использованием Unity. http://blogs.msdn.com/mpuleio/archive/2008/07/17/proof-of-concept-a-simple-di-solution-for-asp-net-webforms.aspx

0 голосов
/ 15 декабря 2008

Я использую реализацию этого интерфейса:

public interface IResolver
{
    object Resolve(Type type);
    object Resolve(string name);

    T Resolve<T>() where T : class;
    T Resolve<T>(string name) where T : class;
}

Который фактически обернут в глобальный статический класс, например:

public static class Resolver // : IResolver
{
    private static IResolver _current;

    public static object Resolve(Type type)
    {
        return Current.Resolve(type);
    }

    public static object Resolve(string name)
    {
        return Current.Resolve(name);
    }

    public static T Resolve<T>() where T : class
    {
        return Current.Resolve<T>();
    }

    public static T Resolve<T>(string name) where T : class
    {
        return Current.Resolve<T>(name);
    }

    private static IResolver Current
    {
        get
        {
            if (_current == null)
            {
                _current = new SpringResolver();
            }

            return _current;
        }
    }
}

Также я пытаюсь следовать простому правилу - используйте класс Resolver как можно меньше, вместо этого вводите сервисы в объекты, которым нужны эти сервисы.

...