我应该将Unity容器传递给我的依赖项吗?

Pet*_*Jr. 4 c# dependency-injection unity-container

所以我有:

应用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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:这是我使用属性注入的方式:

我改变了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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并在ApplicationA中更新了我的Main方法:

    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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Pau*_*les 9

你绝对不想传递容器.您应该查看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))))
Run Code Online (Sandbox Code Playgroud)

然后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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @Peter LaComb Jr.:控制反转的重点是使依赖关系显式化.如果你传入一个容器作为服务定位器,你就不会泄露依赖关系(消费者可以从容器中取出任何东西,如果没有读取代码就不知道它).使依赖项显式,因此不要使用服务定位器,因此不要传入容器. (7认同)