【问题标题】:Unity injecting dependencies down the call stackUnity 在调用堆栈中注入依赖项
【发布时间】:2017-04-11 13:53:42
【问题描述】:

我正在尝试掌握 IoC 容器(目前特别是 Unity),并且对于预先注入所有依赖项的概念有些挣扎。

我的问题与具有构造函数参数的类特别相关,当我最初在容器中注册类型时,该参数的值是未知的。

这里有一个小sn-p,应该能说明我在说什么。

class Class1
{
    IUnityContainer uContainer;

    public Class1()
    {
        uContainer = new UnityContainer();

        uContainer.RegisterType<IRepo, Repo>(new ContainerControlledLifetimeManager()));

        Class2 cls2 = uContainer.Resolve<Class2>();

        cls2.DoSomething();

    }
}

class Class2
{
    IRepo _repo;

    public Class2(IRepo p_repo)
    {
        _repo = p_repo;
    }

    public void DoSomething()
    {
        IType2 typ2 = new Type2(_repo.SomeDataRetrieved());

        Class3 cls3 = new Class3(_repo, typ2);
    }
}

class Class3
{
    IRepo _repo;
    IType2 _type2;

    public Class3(IRepo p_repo, IType2 p_type2)
    {
        _repo = p_repo;
        _type2 = p_type2;
    }
}

我可以在 Class1 中设置容器,并使用 UnityContainer 将 Repo 注入 Class2。在 Class2 中,对 Repo 的一些查找是返回一个 Type2 的实例,该实例只能在 Class2 中实例化。然后我需要将 Repo 与 Type2 创建的新对象一起传递给 Class3。

问题是双重的:

  1. 我无法解析 Class1 中的 Class3,因为它只能在 Class2 中实例化。
  2. 我无法在容器中注册 Type2,因为它实际上必须有一个基于 Class2 中的调用输出的值(不是默认值),该值与 Container 存在并执行的范围不同注册。

就目前而言,我可以使用容器将依赖项注入 Class2,但对于 Class3,我需要创建新实例或使用从 2 到 3 的构造函数注入,这让我想知道为什么我要使用容器然后如果无论如何,我不得不求助于手动注射。

那么,如何在容器中注册 Class3,以便在实例化它时,注入一开始在容器中注册的 repo 的单例以及在 DoSomething 中创建的 Type2 的实例。

提前感谢您的帮助。

【问题讨论】:

    标签: c# dependency-injection unity-container


    【解决方案1】:

    我看到了关于你的方法的多个方面:

    1. 容器和注册应该在 Class1 之前发生。如果您只是将它作为示例编写,并且在您的实际代码中,它在 program 或等效项中,请忽略此注释。
    2. 您应该使用 Unity 查看工厂。例如,在构造 Class2 时,您可以将 Func&lt;IType2, Class3&gt; 传递给它的构造函数,然后在 Class2 中调用该工厂以获取 Class3 实例。这是一个例子:
    container.RegisterType<Func<Type2, Class3>>(
        new InjectionFactory(c =>
        new Func<Type2, Class3>(type2 =>
        c.Resolve<Class3>(
            new InjectionConstructor(
                new ResolvedParameter<IRepo>(),
                type2)))));
    

    注意:没有测试过那个代码,但我做过类似的事情。

    【讨论】:

      【解决方案2】:

      除了Tipx的回答,在做依赖注入的时候,不希望你的类直接创建其他类。

      public void DoSomething()
      {
          IType2 typ2 = new Type2(_repo.SomeDataRetrieved());
          Class3 cls3 = new Class3(_repo, typ2);
      }
      

      这将class2Type2Class3 联系在一起,如果这三个关系密切到没有一个可以交换,这可能没问题,但通常在某个时间点你想换掉@987654326 @ 代表OverchargedType2PlusType2Mock。这就是抽象工厂的用途:给 Class2 一些可以创建 Type2s 的东西。

      interface IType2Factory
      {
           IType2 Create( Data theData );
      }
      
      class Type2Type2Factory : IType2Factory
      {
           public IType2 Create( Data theData ) => new Type2( theData );
      }
      

      (如果Type2 需要进一步的依赖,请参阅this answer

      然后,将IType2Factory发送给Class2

      class Class2
      {
          private readonly IRepo _repo;
          private readonly IType2Factory _type2Factory;
      
          public Class2( IRepo repo, IType2Factory type2Factory )
          {
              _repo = repo;
              _type2Factory = type2Factory;
          }
      
          public void DoSomething()
          {
              var typ2 = _type2Factory.Create( _repo.SomeDataRetrieved() );
          }
      }
      

      当你需要 OverchargedType2Plus 的那一天到来时,创建一个 IType2Factory 实现并注册它,然后观察 Class2 与新的 Type2 变体一起工作,甚至不知道它存在,更不用说需要任何更改了。

      【讨论】:

      • 是的,我第一次有一个关于“你应该考虑使用接口”的部分,但是由于他使用 IType2,我认为其他接口的遗漏只是他写了一个简单的例子。
      • 很公平,我主要想为注册中定义的工厂提供一个替代方案。对我来说,手动编码工厂有三个好处:注册更简单,工厂和产品的定义可以更紧密地联系在一起,产品是工厂的私有嵌套类,最重要的是,您可以在编译时检查您的工厂(如果产品的构造函数发生更改,注入工厂将在运行时进行)...
      • 我花了一段时间才回到这个问题上。 2分,我尽可能使用接口并且我使用工厂,但是对于更简单的任务,没有考虑过那样使用工厂。这绝对有助于思考过程。我还没有解决这个问题,但这绝对是让它正常工作的正确方向。感谢你们俩的意见。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 2012-01-02
      相关资源
      最近更新 更多