当一些参数只有在运行时才知道时,使用依赖注入的正确方法

本文关键字:依赖 注入 方法 参数 运行时 | 更新日期: 2023-09-27 18:14:29

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

  1. 如果我需要使用控制器初始化对象该怎么办?并且字段参数只有在运行时才知道?

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;
    }
}

我认为这是不可以在任何时候调用当我需要对象这个初始化函数或我错了?

根据这个答案不能't组合工厂/DI和依赖注入(DI) "友好"是否有一种模式可以初始化通过DI容器创建的对象

使用抽象工厂如果你需要一个短期对象

通过构造函数注入注入的依赖关系往往是长期存在的,但有时你需要一个短暂存在的对象,或者基于一个只有在运行时才知道的值来构造依赖关系。

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大小写吗?,为什么?或者是别的什么?

当一些参数只有在运行时才知道时,使用依赖注入的正确方法

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

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

  1. 通过API或
  2. 的方法调用传递运行时数据
  3. 从允许解析运行时数据的特定抽象中检索运行时数据。

与普遍的看法相反,抽象工厂几乎不是解决这个问题的正确方法,因为:

工厂不但没有降低消费者的复杂性,反而增加了复杂性,因为消费者现在需要同时依赖于服务抽象IService和抽象工厂IServiceFactory,而不是仅仅依赖于服务抽象IService。[…在对这些类进行单元测试时,可以立即感受到复杂性的增加。这不仅迫使我们测试消费者与服务之间的交互,我们还必须测试与工厂之间的交互。

答案一如既往,要视情况而定。没有上下文就很难给出建议。

然而,有一件事我会犹豫——配置注入"纯"字符串到组件。字符串可以表示数千种含义,以后可能很难支持这种设计。当然,您可以将注入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;
         }
     }

我个人会使用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>();

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