Должен ли я передавать контейнер Unity в мои зависимости? - PullRequest
4 голосов
/ 26 января 2011

Итак, у меня есть:

Применение 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();
        }
    }
}

Редактировать: Вот как я заставил это работать с внедрением свойства:

Я изменил 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);
    }
}

и обновил мой метод Main в 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);
            }
        }
    }

1 Ответ

8 голосов
/ 26 января 2011

Вы определенно не хотите проходить мимо контейнеров. Вы должны посмотреть на поддержку фабрики 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);
  }
}
...