我有一个使用依赖注入的类:
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
中的任何方法都可以全局使用这些值,而无需传递值独立地使用每种方法。
有任何线索吗?
答案 0 :(得分:1)
TL; DR:我们应该将依赖项注入需要它们的类中。类不应该负责为其依赖项提供依赖项。
如果MainClass
从某个地方接收到userId
和sessionId
,它将如何接收它们?大概是将它们注入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)
基本上,将参数传递给实现的构造函数会引入耦合,这与依赖项注入原理相反。
但是,如果您定义了每个实现都必须在构造函数中接收两个参数userId
和sessionId
的要求,则将保留去耦,因为所有实现都遵循此约定。
要启用这种情况,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
不再使用构造函数参数来显式声明其依赖项。现在,该代码不再需要自我记录。