C#如何初始化或将默认构造函数值设置为从类注入的依赖项

时间:2019-07-03 15:28:59

标签: c# dependency-injection ninject

我有一个使用依赖注入的类:

public class MainClass: IDisposable
    {

        IUtilityRepository _utilityRepo = null;

        public MainClass()
        {

        }

        public MainClass(IUtilityRepository utilityRepo)
        {

            _utilityRepo = utilityRepo;

        }
}

然后我的UtilityRepository类具有:

 public class UtilityRepository : IUtilityRepository
    {
        private int _userId;
        private int _sessionId;

        // maybe have here some constructor where i can get some values from MainClass
       public  UtilityRepository ()
       {
          // here set the private properties from the MainClass when the dependency is intialized from MainClass
       }


        public List<string> MethodTestOne(string tempFolder)
        {
// here I want to 
        }

public List<string> MethodTestTwo(string tempFolder)
        {
        }
}

我想要做的是将MainClass的两个属性传递给UtilityRepository类,这样UtilityRepository中的任何方法都可以全局使用这些值,而无需传递值独立地使用每种方法。

  

有任何线索吗?

2 个答案:

答案 0 :(得分:1)

TL; DR:我们应该将依赖项注入需要它们的类中。类不应该负责为其依赖项提供依赖项。

如果MainClass从某个地方接收到userIdsessionId,它将如何接收它们?大概是将它们注入MainClass中,或者注入提供它们的东西。

如果MainClass通过注入接收到它们,UtilityRepository可以以相同的方式接收它们。如果MainClass没有通过注入接收到它们,请配置容器以将它们注入到UtilityRepository中。看起来可能像这样:

public interface IContext // not the greatest name
{
    string UserId { get; }
    string SessionId { get; }
}

public interface IContextAccessor
{
    // where Context is an object containing the values you need.
    Context GetContext();
}

然后,您配置容器以提供IContextAccessor的运行时实现,该实现从当前请求中检索值。将IContextAccessor注入UtilityRepository

public class UtilityRepository : IUtilityRepository
{
    private readonly IContextAccessor _contextAccessor;

    public UtilityRepository(IContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }
}

如果MainClass不需要这些值(它只是接收它们,以便可以将它们传递给其他值),则它不应接收它们。如果MainClass 确实需要它们,那么UtilityRepository也需要它们的事实是偶然的。 (您也可以将IContextAccessor注入MainClass中。)没有理由MainClass负责将它们传递给UtilityRepository

MainClass取决于抽象-IUtilityRepository。它不也不应该知道该接口的具体实现依赖于什么。它根本不应该知道该界面中没有的任何内容。一旦这样做,它就不再“依赖”接口。它与实现耦合。

这是使用IoC容器的主要好处之一。类从容器而不是彼此获取它们的依赖关系。依赖抽象意味着类不知道这些抽象是如何实现的。反过来,这意味着类不知道它们的依赖项 of 的依赖项。


举例说明:一盏灯取决于抽象-一个从某处获取电力的电源插座。它可能是电网,太阳能电池板,发电机,自行车或其他任何东西其他。

无论电源的实现是什么,它都可能具有自己的依赖性。发电机需要汽油。电网需要发电厂。

这些依赖项可能具有自己的依赖项。发电厂需要有人铲煤。

灯不应该了解那些依赖关系。如果使用灯表示:

  • 插入灯
  • 打开灯
  • 将气体注入发生器

然后,我们不再依赖抽象。我们依靠发电机。我们是否将其声明为接口并命名为IPowerSupply都没有关系。它可以使用的唯一实现是生成器。

电网也是如此。它取决于电源。该电源可能需要煤炭,发电机等。但是,如果让电网负责启动发电机,然后又要用大型太阳能电池板阵列替换发电机,会发生什么呢?没有发电机时,电网将如何启动发电机?

这就是每个类都应尽可能少地了解其依赖项的原因。如果知道什么,它就与该细节相关。依赖倒置(取决于抽象)的全部目的是防止或最小化耦合,以便更改一个实现细节不会产生连锁反应,这种连锁反应会迫使我们更改其他不必更改的内容。

IoC容器使我们轻松完成此任务。每个类通过在构造函数中要求它来说明需要什么,然后容器将其注入。如果注入的事物具有其自己的依赖关系,则容器也将处理该依赖关系。我们所有的类都是独立构造的,彼此之间一无所知。

答案 1 :(得分:0)

基本上,将参数传递给实现的构造函数会引入耦合,这与依赖项注入原理相反。

但是,如果您定义了每个实现都必须在构造函数中接收两个参数userIdsessionId的要求,则将保留去耦,因为所有实现都遵循此约定。

要启用这种情况,MainClass必须接收IKernel参数,而不是IUtilityRepository

public class MainClass
{
    private readonly IUtilityRepository _utilityRepo = null;

    public MainClass()
    {
    }

    public MainClass(IKernel kernel)
    {
        var userId = new Ninject.Parameters.ConstructorArgument("userId", 123);
        var sessionId = new Ninject.Parameters.ConstructorArgument("sessionId", 456);

        _utilityRepo = kernel.Get<IUtilityRepository>(userId, sessionId);
    }
}

通过这种方式,UtilityRepository将能够在构造函数中接收两个参数:

public class UtilityRepository : IUtilityRepository
{
    private readonly int _userId;
    private readonly int _sessionId;

    // maybe have here some constructor where i can get some values from MainClass
    public  UtilityRepository (int userId, int sessionId)
    {
        _userId = userId;
        _sessionId = sessionId;
    }

    public List<string> MethodTestOne(string tempFolder)
    {
        // do something...
    }

    public List<string> MethodTestTwo(string tempFolder)
    {
        // do something...
    }
}

请注意,在这种情况下,UtilityRepository的注册必须是暂时的,以便每次使用特定参数值请求新实例时都会创建一个新实例:

kernel.Bind<IUtilityRepository>().To<UtilityRepository>().InTransientScope();

该解决方案的一个缺点是MainClass不再使用构造函数参数来显式声明其依赖项。现在,该代码不再需要自我记录。