在某些参数仅在运行时知道时使用依赖注入的正确方法

时间:2016-10-24 07:37:39

标签: c# dependency-injection ninject inversion-of-control

我在Ninject wiki上阅读了一堆问题和所有文档,但我仍然怀疑并向自己提出问题,例如"我做得对吗?"

  1. 如果我需要使用控制器初始化对象怎么办?只有运行时才能知道字段参数?
  2. A)所以我需要这样的东西:

        public class MyClass : IMyInterface
        {
             private string _field;
             readonly IRepository _repo;
             public MyClass(string field, IRepository repo)
             {
                 _field = field;
                 _repo = repo;
             }
         }
    

    但如何正确捆绑?或者我应该永远忘记使用构造函数,但构造函数依赖注入?

    B)并且这样做:

    public class MyClass : IMyInterface
    {
        private string _field;
        readonly IRepository _repo;
        public MyClass(IRepository repo)
        {
            _repo = repo;
        }
        public void Initialize(string field)
        {
            _field = field;
        }
    }
    

    我认为当我需要对象初始化函数或者我错了时,随时打电话都不行吗?

    根据此答案Can't combine Factory / DIDependency Inject (DI) "friendly" library以及Is there a pattern for initializing objects created via a DI container

      

    如果您需要短期对象,请使用Abstract Factory

         

    使用构造函数注入注入的依赖项往往是长期存在的,但有时您需要一个短期对象,或基于仅在运行时知道的值构建依赖项。

    C)我应该这样做:

    public interface IMyInterface  {  }
      public interface IMyFactory
      {
          MyClass Create(string field);
      } 
      public class MyFactory : IMyFactory
      {
          private readonly IRepository _repo;
          public MyFactory (IRepository repo)
          {
              _repo = repo;
          }
          public MyClass Create(string field)
          {
              return new MyClass(_repo, field);
          }
      }
      public class MyClass : IMyInterface
      {
          private string _field;
          readonly IRepository _repo;
          public MyClass(IRepository repo, string field)
          {
              _repo = repo;
              _field = field;
          }
      }
    

    如果我需要来自其他课程的MyClass,我会像

    一样使用它
    public class OtherClass
    {
        private IMyInterface _myClass;
        public OtherClass(IMyFactory factory)
        {
            _myClass = factory.Create("Something");
        }
    }
    

    不是太笨重了吗?

    我的问题是:我必须使用A,B或C案例?为什么?或者别的什么?

3 个答案:

答案 0 :(得分:2)

  

如果我需要使用控制器初始化对象怎么办?而字段参数只能在运行时知道吗?

正如here所解释的那样,您的应用程序组件在初始化期间不应要求运行时数据。相反,你应该:

  1. 通过API或
  2. 的方法调用传递运行时数据
  3. 从允许解析运行时数据的特定抽象中检索运行时数据。
  4. 与流行的看法相反,抽象工厂几乎不是解决这个问题的正确方法,因为:

      

    而不是降低消费者的复杂性,工厂实际上增加了复杂性,因为消费者现在需要依赖于IService和Abstract Factory IServiceFactory,而不仅仅依赖于服务抽象IService。 [...]单元测试此类时,可以立即感受到复杂性的增加。这不仅迫使我们测试消费者与服务的互动,我们也必须测试与工厂的互动。

    有关详细讨论,请阅读this article

答案 1 :(得分:1)

答案是,一如既往,取决于它。没有上下文,就很难建议。

然而,有一件事我会犹豫不决 - 配置注入"纯"字符串到组件。字符串可能意味着成千上万的事情,以后可能很难支持该设计。当然,您可以定义注入XController的字符串将是SQL的连接字符串,当注入YController时将是一个授权令牌,但我怀疑它是否可读且易于维护。

据说我会选择 C 选项或以下方法:

    public class DescriptiveMeaningOfAClass
    {
         public string Value { get; set; }
     }

    public class MyClass : IMyInterface
    {
         private string _field;
         readonly IRepository _repo;
         public MyClass(DescriptiveMeaningOfAClass something, IRepository repo)
         {
             _field = something.Value;
             _repo = repo;
         }
     }

答案 2 :(得分:0)

Personnaly我会通过使用ChildKernel而不是直接引用构造函数来混合A)和C)。 https://github.com/ninject/Ninject.Extensions.ChildKernel

在你的工厂:

var parentKernel = new StandardKernel();

var childKernel1 = new ChildKernel(parentKernel);
childKernel1.Bind<string>().ToConstant("MyRuntimeValue");
var myClass = childKernel1.Get<MyClass>();

我没有测试它,但它应该可以工作。