依赖注入容器 - 如何保持可用
Dependency Injection Container - How to keep available to
在使用依赖注入创建应用程序时,它利用依赖注入框架,例如 Unity(或 Ninject)。
您如何在一开始就初始化将接口注册到容器,并使它们可供应用程序在其 运行 应用程序的整个生命周期中使用?
您是否需要将 DI 容器传递给可能利用依赖注入的每个方法,或者是否有某种方法可以使容器全局可访问,以便您可以在开始时将它们一起注册并在整个 运行 应用程序中访问它们不必不断地通过它们,并且能够在需要时使用它们?
环境:Visual Studio2015、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<IGame, TrivialPursuit>();
var gameInstance = diContainer.Resolve<IGame>();
var xotherClass = new AnotherClass();
xotherClass.TestOtherClassOtherMethod();
}
------ 另一个 class 没有依赖注入的上下文 Class ------
public void TestOtherClassOtherMethod()
{
IGame gameInstance = -- -Container is Not available to resolve from in this class ---
}
原因:我不想将我以后可能需要的所有可能类型传递给我加载的每个 class,我只想在需要时使用这些实例。我对 classes 的研究越深入,后来随着应用程序变得越来越复杂,我不想将每种类型的实例从 Main() 方法向上传递给每个 class.
依赖注入 (DI) 容器就是这样。促进 DI 的框架。您不会为了解析对象的实例而四处传递容器。您只需在 classes 构造函数中请求所需的类型,DI 框架就会注入适当的依赖项。
Mark Seemann 写了一篇很好的文章 book on dependency injection 我会推荐。
您在组合根目录中注册所有需要用容器解析的内容。也就是说你的程序启动的时候就是应该注册的时候。
假设我们有以下代码:
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。
我们应该做的第一件事是重构 classes 以通过它们的构造函数获取它们的依赖关系,并依赖于接口而不是实体。我们无法注入依赖项,除非有地方可以注入它们(构造函数、属性 等)。
这是重构后的代码:
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()
{
}
}
您会注意到 classes 现在都通过它们的构造函数获取它们的依赖关系,而不是 new 任何东西。 类 应该只接受他们实际需要的依赖项。例如,MyClass 不需要 Dependency2,因此它不会要求一个。它只要求 Dependency1,因为这就是它所需要的。 Dependency1 需要 Dependency2,而不是 MyClass。
现在要在没有容器的情况下将其全部连接起来,我们只需在组合根中将其全部新建即可:
void Main()
{
var myClass = new MyClass(new Dependency1(new Dependency2()));
}
你可以看到如果我们有大量的 classes 和依赖项,那会变得多么麻烦。这就是我们使用容器的原因。它为我们处理所有的依赖图。使用容器,我们将其重写如下:
void Main()
{
// the order of our registration does not matter.
var container = new Container();
container.Register<IDependency1>.For<Dependency1>();
container.Register<IDependency2>.For<Dependency2>();
container.Register<IMyClass>.For<MyClass>();
// then we request our first object like in the first example (MyClass);
var myClass = container.Resolve<IMyClass>();
myClass.Run();
}
在第二个示例中,容器将处理连接所有依赖项。所以我们永远不需要将 Depedency2 传递给 MyClass,然后再传递给 Depedency1。我们只需要在 Dependency1 中请求它,容器就会像第一个例子一样为我们连接它。
所以在你的例子中我们会这样重写它:
static void Main(string[] args)
{
var game = new UnityContainer();
game.RegisterType<IGame, TrivialPursuit>();
game.RegisterType<IAnotherClass, AnotherClass>();
game.RegisterType<IYetAnotherClass, YetAnotherClass>();
var gameInstance = game.Resolve<IGame>();
// 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 {}
在这些情况下,无需传递容器。您向容器注册依赖项,然后在 classes 构造函数中请求它们。如果您希望在 class 中使用容器而不通过构造函数请求它,那么您不是在进行 DI,您只是将容器用作单例服务定位器。通常应该避免的事情。
容器即服务定位器
通常应该避免这种情况,但如果您想将容器用作服务定位器,您有两个选择:
1) 通过构造函数将容器传递给需要它的 classes。
您可以使用上述示例为 DI 连接 classes。但不是在构造函数中请求像 IDependency 这样的依赖项,而是传递容器。
public class Game : IGame
{
public Game(IContainer container)
{
var blah = container.Resolve<IBlah>();
}
}
2) 通过静态 class:
请求容器
public static class ServiceLocator
{
private static IContainer container;
public static IContainer Container
{
get
{
if (container == null)
{
container = new Container();
}
return container;
}
}
}
使用 ServiceLocator class 在组合根中正常注册所有内容。然后使用:
public class MyClass
{
public void DoSomething()
{
var blah = ServiceLocator.Container.Resolve<IBlah>();
}
}
在使用依赖注入创建应用程序时,它利用依赖注入框架,例如 Unity(或 Ninject)。
您如何在一开始就初始化将接口注册到容器,并使它们可供应用程序在其 运行 应用程序的整个生命周期中使用?
您是否需要将 DI 容器传递给可能利用依赖注入的每个方法,或者是否有某种方法可以使容器全局可访问,以便您可以在开始时将它们一起注册并在整个 运行 应用程序中访问它们不必不断地通过它们,并且能够在需要时使用它们?
环境:Visual Studio2015、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<IGame, TrivialPursuit>();
var gameInstance = diContainer.Resolve<IGame>();
var xotherClass = new AnotherClass();
xotherClass.TestOtherClassOtherMethod();
}
------ 另一个 class 没有依赖注入的上下文 Class ------
public void TestOtherClassOtherMethod()
{
IGame gameInstance = -- -Container is Not available to resolve from in this class ---
}
原因:我不想将我以后可能需要的所有可能类型传递给我加载的每个 class,我只想在需要时使用这些实例。我对 classes 的研究越深入,后来随着应用程序变得越来越复杂,我不想将每种类型的实例从 Main() 方法向上传递给每个 class.
依赖注入 (DI) 容器就是这样。促进 DI 的框架。您不会为了解析对象的实例而四处传递容器。您只需在 classes 构造函数中请求所需的类型,DI 框架就会注入适当的依赖项。
Mark Seemann 写了一篇很好的文章 book on dependency injection 我会推荐。
您在组合根目录中注册所有需要用容器解析的内容。也就是说你的程序启动的时候就是应该注册的时候。
假设我们有以下代码:
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。
我们应该做的第一件事是重构 classes 以通过它们的构造函数获取它们的依赖关系,并依赖于接口而不是实体。我们无法注入依赖项,除非有地方可以注入它们(构造函数、属性 等)。
这是重构后的代码:
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()
{
}
}
您会注意到 classes 现在都通过它们的构造函数获取它们的依赖关系,而不是 new 任何东西。 类 应该只接受他们实际需要的依赖项。例如,MyClass 不需要 Dependency2,因此它不会要求一个。它只要求 Dependency1,因为这就是它所需要的。 Dependency1 需要 Dependency2,而不是 MyClass。
现在要在没有容器的情况下将其全部连接起来,我们只需在组合根中将其全部新建即可:
void Main()
{
var myClass = new MyClass(new Dependency1(new Dependency2()));
}
你可以看到如果我们有大量的 classes 和依赖项,那会变得多么麻烦。这就是我们使用容器的原因。它为我们处理所有的依赖图。使用容器,我们将其重写如下:
void Main()
{
// the order of our registration does not matter.
var container = new Container();
container.Register<IDependency1>.For<Dependency1>();
container.Register<IDependency2>.For<Dependency2>();
container.Register<IMyClass>.For<MyClass>();
// then we request our first object like in the first example (MyClass);
var myClass = container.Resolve<IMyClass>();
myClass.Run();
}
在第二个示例中,容器将处理连接所有依赖项。所以我们永远不需要将 Depedency2 传递给 MyClass,然后再传递给 Depedency1。我们只需要在 Dependency1 中请求它,容器就会像第一个例子一样为我们连接它。
所以在你的例子中我们会这样重写它:
static void Main(string[] args)
{
var game = new UnityContainer();
game.RegisterType<IGame, TrivialPursuit>();
game.RegisterType<IAnotherClass, AnotherClass>();
game.RegisterType<IYetAnotherClass, YetAnotherClass>();
var gameInstance = game.Resolve<IGame>();
// 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 {}
在这些情况下,无需传递容器。您向容器注册依赖项,然后在 classes 构造函数中请求它们。如果您希望在 class 中使用容器而不通过构造函数请求它,那么您不是在进行 DI,您只是将容器用作单例服务定位器。通常应该避免的事情。
容器即服务定位器 通常应该避免这种情况,但如果您想将容器用作服务定位器,您有两个选择:
1) 通过构造函数将容器传递给需要它的 classes。 您可以使用上述示例为 DI 连接 classes。但不是在构造函数中请求像 IDependency 这样的依赖项,而是传递容器。
public class Game : IGame
{
public Game(IContainer container)
{
var blah = container.Resolve<IBlah>();
}
}
2) 通过静态 class:
请求容器public static class ServiceLocator
{
private static IContainer container;
public static IContainer Container
{
get
{
if (container == null)
{
container = new Container();
}
return container;
}
}
}
使用 ServiceLocator class 在组合根中正常注册所有内容。然后使用:
public class MyClass
{
public void DoSomething()
{
var blah = ServiceLocator.Container.Resolve<IBlah>();
}
}