2014-10-12 2 views
1

В настоящее время я изучаю API для Autofac, и я пытаюсь понять, что мне кажется очень распространенным случаем использования.Autofac: Разрешение зависимостей с параметрами

У меня есть класс (для этого простого примера «MasterOfPuppets»), который имеет зависимость, которую он получает с помощью инъекции конструктора («NamedPuppet»), эта зависимость требует значение, которое будет построено с (название строки):

public class MasterOfPuppets : IMasterOfPuppets 
 
    { 
 
     IPuppet _puppet; 
 

 
     public MasterOfPuppets(IPuppet puppet) 
 
     { 
 
      _puppet = puppet; 
 
     } 
 
    } 
 

 
    public class NamedPuppet : IPuppet 
 
    { 
 
     string _name; 
 

 
     public NamedPuppet(string name) 
 
     { 
 
      _name = name; 
 
     } 
 
    }

зарегистрировать оба класса с их интерфейсами, и чем я хочу решить IMasterOfPuppets, со строкой, которая будет вводится в экземпляр «NamedPuppet».

я попытался сделать это следующим образом:

IMasterOfPuppets master = bs.container.Resolve<IMasterOfPuppets>(new NamedParameter("name", "boby"));

Это заканчивается с ошибкой времени выполнения, так что я думаю Autofac только пытается внедрить его в «MasterOfPuppets».

Итак, мой вопрос в том, как я могу разрешить только «IMasterOfPuppets» и передать аргументы параметров своей зависимости, самым элегантным способом? У других контейнеров ioc есть лучшие решения для этого?

ответ

6

Autofac не поддерживает передачу параметров родительскому/потребительскому объекту, и эти параметры стекают вниз на дочерние объекты.

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

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

public class ConfigurablePuppet : IPuppet 
{ 
    private string _name; 
    public ConfigurablePuppet(string name) 
    { 
    this._name = ConfigurationManager.AppSettings[name]; 
    } 
} 

Две вещи, чтобы отметить там.

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

Во-вторых, как пример NamedPuppet и новый ConfigurablePuppet принимать строковый параметр с тем же именем, но это означает что-то другое для реализации основы. Поэтому, если ваш потребительский код делает то, что вы показываете в примере, - передавая параметр, который должен быть , имя - то у вас, вероятно, есть проблема с дизайном интерфейса. См.: Liskov substitution principle.

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

Что касается параметров прохождения, Autofac имеет поддержку параметров.

Рекомендованный и наиболее распространенный тип передачи параметров является during registration, потому что в это время вы можете настроить все на уровне контейнера и вы не используете сервисный центр (который generally considered an anti-pattern).

Если вам нужно сдать параметры во время разрешения Autofac also supports that. Однако при прохождении во время разрешения, это больше сервис-локатор-иш и не так уж велико, потому что, опять же, это означает, что потребитель знает, что он потребляет.

Вы можете сделать некоторые причудливые вещи с помощью lambda expression registrations, если вы хотите подключить параметр, поступающий от известного источника, например конфигурации.

builder.Register(c => { 
    var name = ConfigurationManager.AppSettings["name"]; 
    return new NamedPuppet(name); 
}).As<IPuppet>(); 

Вы можете также сделать некоторые модные вещи, используя the Func<T> implicit relationship в потребителе:

public class MasterOfPuppets : IMasterOfPuppets 
{ 
    IPuppet _puppet; 

    public MasterOfPuppets(Func<string, IPuppet> puppetFactory) 
    { 
     _puppet = puppetFactory("name"); 
    } 
} 

Делать это является эквивалентом использования TypedParameter типа string при разрешении. Но, как вы можете видеть, это исходит от прямого потребителя IPuppet, а не что-то, что стекает сквозь стек всех разрешений.

Наконец, вы также можете использовать Autofac modules, чтобы сделать некоторые интересные сквозные вещи так, как вы видите в log4net integration module example. Используя подобный метод, вы можете вставить конкретный параметр во всем мире по всем разрешениям, но он не обязательно обеспечивает возможность передачи параметра во время выполнения - вам нужно будет поместить источник параметра внутри модуля.

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

Надеюсь, это должно заставить вас двигаться в правильном направлении.

+2

Я не совсем согласен с конструктивным следствием этого. Пример, который я дал, был простым, может быть, слишком простым. Но в реальном решении имеет смысл передать определенный контекст/состояние и просачиваться во все зависимости, требующие его. В любом случае, я проверил документацию для Unity, и эта функция фактически поддерживается в форме ["ParameterOverrides"] (http://msdn.microsoft.com/en-us/library/ff660920 (v = pandp.20) .aspx) Итак, вопрос в том, есть ли аналогичная функция в Autofac или другом контейнере ioc (рядом с Unity, который является довольно тяжелым). – JoefGoldstein

+0

Funcs/factories в Autofac фактически позволяют передавать данные при разрешении экземпляров. Я думаю, что это было бы правильное решение проблемы OP. –

+0

Извинения за короткий первоначальный ответ - я был на своем телефоне в выходные и хотел попробовать разблокировать OP. Я расширил ответ и уточнил. –

Смежные вопросы