2011-01-26 2 views
4

Так у меня есть:Должен ли я передавать Контейнер Unity в мои зависимости?

Применение A: Требует класса B (различные сборки)

Класс B: Требуется Класс C (опять же, по-разному монтаж)

Класс C: Использует контейнер для решения различных объектов , но время жизни контейнера (и объектов, которые он разрешает) должно контролироваться корнем композиции.

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

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

Я считаю, что этот источник настолько чист и прост, насколько я могу получить:

namespace InjectionTest 
{ 
    using System; 
    using Microsoft.Practices.Unity; 

    public class ApplicationA 
    { 
     static void Main(string[] args) 
     { 
      using (IUnityContainer container = new UnityContainer()) 
      { 
       // Normally I'd use this, but for clarity in the example, I'm doing it in code. 
       //container.LoadConfiguration(); 
       container.RegisterType<IClassB, ClassB>(); 
       container.RegisterType<IClassC, ClassC>(); 
       container.RegisterType<IFooBuilder, FrobBuilder>("frob"); 
       container.RegisterType<IFooBuilder, WidgetBuilder>("widget"); 
       IClassB machine = container.Resolve<IClassB>(); 
       InitialObject bar = new InitialObject() { Name = "widget" }; 
       machine.doSomethingWithBar(bar); 
       bar = new InitialObject() { Name = "frob" }; 
       machine.doSomethingWithBar(bar); 
      } 
     } 
    } 

    public class ClassB : IClassB 
    { 
     IClassC classC { get; private set; } 

     public ClassB(IClassC classc) 
     { 
      this.classC = classc; 
     } 

     public void doSomethingWithBar(InitialObject bar) 
     { 
      var foo = this.classC.BuildMyFoo(bar); 
      /* 
      * Do something else with foo & bar 
      * */ 
     } 

    } 

    public interface IClassB 
    { 
     void doSomethingWithBar(InitialObject bar); 
    } 

    public class ClassC : IClassC 
    { 
     public ResultObject BuildMyFoo(InitialObject bar) 
     { 
      IFooBuilder builder = null; 
      //How best do I get my container here? 
      //IFooBuilder builder = container.Resolve<IFooBuilder>(bar.Name); 
      return builder.build(bar); 
     } 
    } 

    public interface IClassC 
    { 
     ResultObject BuildMyFoo(InitialObject bar); 
    } 

    public class InitialObject 
    { 
     public string Name { get; set; } 
    } 

    public class ResultObject 
    { 
     public string SomeOtherData { get; set; } 
    } 

    public interface IFooBuilder 
    { 
     ResultObject build(InitialObject bar); 
    } 

    public class FrobBuilder : IFooBuilder 
    { 
     public ResultObject build(InitialObject bar) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public class WidgetBuilder : IFooBuilder 
    { 
     public ResultObject build(InitialObject bar) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 

Edit: Это, как я сделал это работать с впрыском собственности:

я изменил ClassC:

public class ClassC : IClassC 
{ 
    [Dependency] 
    public IUnityContainer Container { get; set; } 

    public ResultObject BuildMyFoo(InitialObject bar) 
    { 
     IFooBuilder builder = null; 
     //How best do I get my container here? 
     builder = Container.Resolve<IFooBuilder>(bar.Name); 
     return builder.build(bar); 
    } 
} 

и обновленный мой основной метод в ApplicationA:

public void Main() 
    { 
     using (IUnityContainer container = new UnityContainer()) 
     { 
      // Normally I'd use this, but for clarity in the example, I'm doing it in code. 
      //container.LoadConfiguration(); 
      container.RegisterType<IClassB, ClassB>(); 
      container.RegisterType<IClassC, ClassC>(); 
      container.RegisterType<IFooBuilder, FrobBuilder>("frob"); 
      container.RegisterType<IFooBuilder, WidgetBuilder>("widget"); 
      using (IUnityContainer child = container.CreateChildContainer()) 
      { 
       container.RegisterInstance<IUnityContainer>(child); 
       IClassB machine = container.Resolve<IClassB>(); 
       InitialObject bar = new InitialObject() { Name = "widget" }; 
       machine.doSomethingWithBar(bar); 
       bar = new InitialObject() { Name = "frob" }; 
       machine.doSomethingWithBar(bar); 
      } 
     } 
    } 
+0

OK - Так что я закончил этот пример и получил это работает. инъекция свойств выглядит как путь, мне просто нужно зарегистрировать дочерний контейнер в качестве экземпляра, который я хочу использовать (регистрация результатов верхнего уровня в результате переполнения стека). Я все равно буду благодарен за любые ответы, так как я новичок в этом деле. –

+0

Хорошо, теперь я вижу здесь http://stackoverflow.com/questions/827756/can-a-unity-container-pass-a-reference-of-itself-as-a-constructor-parameter, что я даже не должны создать ребенка - я просто должен отметить свойство как зависимость, и единство автоматически вводит текущий контейнер. Я думаю, что это еще чище. –

+0

Связанный: http://stackoverflow.com/questions/2386487/is-it-better-to-create-a-singleton-to-access-unity-container-or-pass-it-through-t –

ответ

8

Вы определенно не хотите проходить вокруг контейнеров. Вы должны заглянуть в поддержку фабрики Unity, которая будет работать в этой ситуации. Что-то вроде этого:

container.RegisterType<IFooBuilder, FrobBuilder>("Frob") 
     .RegisterType<IFooBuilder, WidgetBuilder>("Widget") 
     .RegisterType<Func<string, IFooBuilder>>(new InjectionFactory(c => new Func<string, IFooBuilder>(barName => c.Resolve<IFooBuilder>(barName)))) 

, а затем ClassC бы параметр конструктора Func:

public class ClassC : IClassC 
{ 
    private readonly Func<string, IFooBuilder> _builderFactory; 

    public ClassC(Func<string, IFooBuilder> builderFactory) 
    { 
    _builderFactory = builderFactory; 
    } 

    public ResultObject BuildMyFoo(InitialObject bar) 
    { 
    IFooBuilder builder = _builderFactory(bar.Name); 
    return builder.build(bar); 
    } 
} 
+0

Но не делает, t это привязать мое приложение ApplicationA к интерфейсу IFooBuilder более тесно, чем использовать дочерний контейнер? (с дочерним контейнером типы, возвращаемые и принятые IFooBuilder, могут меняться без каких-либо изменений в ApplicationA). –

+0

С другой стороны, какой недостаток заключается в передаче детского контейнера? –

+7

@Peter LaComb Jr .: Точка инверсии управления заключается в том, чтобы сделать зависимости явными. Если вы передаете контейнер в качестве локатора служб, вы не обнаруживаете зависимости (потребители могут вытащить что-нибудь из контейнера, и вы не знаете его, не читая код). Делайте зависимости явными, поэтому не используйте локатор сервисов и, следовательно, не проходите в контейнер. – jason

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