使用依赖注入创建应用程序时,它使用依赖注入的框架,例如Unity(或Ninject).
如何初始化一起开始将容器的接口注册到容器,并让它们可供应用程序在整个应用程序的运行生命周期中使用?
您是否需要将DI容器传递给可能使用依赖项注入的每个方法,或者是否有某种方法可以使容器全局可访问,以便您可以在开始时将它们全部注册在一起并在运行应用程序时访问它们而无需持续通过它们,并在需要时能够使用它们?
环境:Visual Studio 2015,C#,Microsoft Unity(用于DI容器)
示例代码
static void Main(string[] args) { // Make Unity resolve the interface, providing an instance // of TrivialPursuit class var diContainer = new UnityContainer(); diContainer.RegisterType(); var gameInstance = diContainer.Resolve (); var xotherClass = new AnotherClass(); xotherClass.TestOtherClassOtherMethod(); }
------没有依赖注入类上下文的另一个类------
public void TestOtherClassOtherMethod() { IGame gameInstance = -- -Container is Not available to resolve from in this class --- }
原因:我不想将以后可能需要的所有类型传递给我加载的每个类,我只想在需要时使用实例.我进入类越深入,随后应用程序变得越来越复杂,我不想将每个类型的实例从Main()方法传递给每个类.
依赖注入(DI)容器就是这样.促进DI的框架.您不会传递容器以解析对象的实例.您只需在类构造函数中请求所需的类型,DI框架将注入适当的依赖项.
Mark Seemann写了一本关于依赖注入的好书,我建议.
您可以使用组合根中的容器注册需要解决的所有内容.也就是说,当您的程序启动时,应该注册所有内容.
假设我们有以下代码:
public class MyClass { public Run() { var dependency = new Dependency1(); dependency.DoSomething(); } } public class Dependency1 { public void DoSomething() { var dependency = new Dependency2(); dependeny.DoSomethingElse(); } } public class Dependency2 { public void DoSomethingElse() { } }
这给了我们上面的依赖链:MyClass - > Dependency1 - > Dependency2.
我们应该做的第一件事是重构类以通过它们的构造函数获取它们的依赖关系并依赖于接口而不是结构.除非有注入它们的地方(构造函数,属性等),否则我们不能注入依赖项.
这是重构的代码:
public interface IMyClass { void Run(); } public interface IDependency1 { void DoSomething(); } public interface IDependency2 { void DoSomethingElse(); } public class MyClass : IMyClass { public readonly IDependency1 dep; public MyClass(IDependency1 dep) { this.dep = dep; } public void Run() { this.dep.DoSomething(); } } public class Dependency1 : IDependency1 { public readonly IDependency2 dep; public MyClass(IDependency2 dep) { this.dep = dep; } public void DoSomething() { this.dep.DoSomethingElse(); } } public class Dependency2 : IDependency2 { public void DoSomethingElse() { } }
你会注意到这些类现在都通过它们的构造函数来获取它们的依赖关系并且不需要新的东西.类应该只接受它们实际需要的依赖项.例如,MyClass不需要Dependency2,所以它不需要一个.它只询问Dependency1,因为这就是它所需要的一切.Dependency1 NEEDS Dependency2,而不是MyClass.
现在连接所有没有容器,我们只需要在组合根中新建它:
void Main() { var myClass = new MyClass(new Dependency1(new Dependency2())); }
如果我们有大量的类和依赖,你可以看到如何变得麻烦.这就是我们使用容器的原因.它为我们处理所有的depdency图.有了容器,我们会按如下方式重写它:
void Main() { // the order of our registration does not matter. var container = new Container(); container.Register.For (); container.Register .For (); container.Register .For (); // then we request our first object like in the first example (MyClass); var myClass = container.Resolve (); myClass.Run(); }
在第二个示例中,容器将处理所有依赖项的连接.因此,我们永远不需要将Depedency2传递给MyClass,然后传递给Depedency1.我们只需要在Dependency1中请求它,容器就会像第一个例子那样为我们连接它.
所以在你的例子中我们会像这样重写它:
static void Main(string[] args) { var game = new UnityContainer(); game.RegisterType(); game.RegisterType (); game.RegisterType (); var gameInstance = game.Resolve (); // you'll need to perform some action on gameInstance now, like gameInstance.RunGame() or whatever. } public class Game : IGame { public Game(IAnotherClass anotherClass) { } } public class AnotherClass : IAnotherClass { public AnotherClass(IYetAnotherClass yetAnotherClass) { } } public class YetAnotherClass : IYetAnotherClass {}
在这些情况下,无需绕过容器.您使用容器注册依赖项,然后在类构造函数中请求它们.如果您希望在类中使用容器而不通过构造函数请求它,那么您不是在进行DI,而只是将容器用作单例服务定位器.通常应避免的事情.
容器作为服务定位器 通常应避免这种情况,但如果要将容器用作服务定位器,则有两种选择:
1)通过构造函数将容器传递到需要它的类中.您可以使用上面的示例为DI类连接类.但是,不要在构造函数中请求IDependency之类的依赖项,而只是传递容器.
public class Game : IGame { public Game(IContainer container) { var blah = container.Resolve(); } }
2)通过静态类请求您的容器:
public static class ServiceLocator { private static IContainer container; public static IContainer Container { get { if (container == null) { container = new Container(); } return container; } } }
使用ServiceLocator类在组合根目录中正常注册所有内容.然后使用:
public class MyClass { public void DoSomething() { var blah = ServiceLocator.Container.Resolve(); } }