所以从我读过的内容来看,最理想的做法是在需要访问IOC时注入内核...好吧,这很好;我们确实将SL的使用保持在最低限度。
不,将内核注入业务类并不是最佳选择。更好的方法是创建一个工厂,例如
IFooFactory { IFoo Create(); }
或
Func<IFoo>
,并让它创建新实例。
该接口的实现放在组合根中,获取内核的实例并使用内核进行解析。这样可以使您的类不受特定容器的限制,并且可以在使用不同容器的另一个项目中重用它们。对于Func,您可以使用以下模块:
Ninject是否支持Func(自动生成的工厂)? Ninject 2.4将原生支持此功能。
就重构而言,如果不了解您应用程序的源代码,很难告诉您最佳方法。我可以提供一种可能可行的方法。
我想您可能希望在长期内对整个应用程序进行适当的 DI 重构。我曾经为一个相当大的项目(30-40人年)做过以下工作:
从组合根开始,自上而下遍历对象树,并一个接一个地更改类以使用适当的 DI。一旦到达所有叶子节点,请开始重构所有不依赖于其他服务的服务,并使用相同的方法向其叶子节点工作。之后,继续处理仅依赖于已经重构的服务的服务,并重复此过程,直到所有服务都被重构。所有这些步骤都可以依次完成,以便在新功能仍然可以添加的同时持续完善代码。与此同时,只要专注尽快做好,ServiceLocation 就是可以接受的。
伪代码示例:
Foo{ ServiceLocator.Get<Service1>(), new Bar() }
Bar{ ServiceLocator.Get<IService1>(), ServiceLocator.Get<IService2>(), new Baz() }
Baz{ ServiceLocator.Get<IService3>() }
Service1 { ServiceLocator.Get<Service3>() }
Service2 { ServiceLocator.Get<Service3>() }
Service3 { new SomeClass()}
步骤1 - 更改根目录(Foo)
Foo{ ctor(IService1, IBar) }
Bar{ ServiceLocator.Get<IService1>(), ServiceLocator.Get<IService2>(), new Baz() }
Baz{ ServiceLocator.Get<IService3>() }
Service1 { ServiceLocator.Get<IService2>() }
Service2 { ServiceLocator.Get<IService3>() }
Service3 { new SomeClass()}
Bind<IBar>().To<Bar>();
Bind<IService1>().ToMethod(ctx => ServiceLocator.Get<IService1>());
步骤2 - 更改根目录的依赖关系
Foo{ ctor(IService1, IBar) }
Bar{ ctor(IService1, IService2, IBaz) }
Baz{ ServiceLocator.Get<IService3>() }
Service1 { ServiceLocator.Get<Service2>() }
Service2 { ServiceLocator.Get<Service3>() }
Service3 { new SomeClass()}
Bind<IBar>().To<Bar>();
Bind<IBaz>().To<Baz>();
Bind<IService1>().ToMethod(ctx => ServiceLocator.Get<IService1>());
Bind<IService2>().ToMethod(ctx => ServiceLocator.Get<IService2>());
步骤三 - 更改它们的依赖关系并继续,直到到达叶子节点
Foo{ ctor(IService1, IBar) }
Bar{ ctor(IService1, IService2, IBaz) }
Baz{ ctor(IService3) }
Service1 { ServiceLocator.Get<Service2>() }
Service2 { ServiceLocator.Get<Service3>() }
Service3 { new SomeClass() }
Bind<IBar>().To<Bar>();
Bind<IBaz>().To<Baz>();
Bind<IService1>().ToMethod(ctx => ServiceLocator.Get<IService1>());
Bind<IService2>().ToMethod(ctx => ServiceLocator.Get<IService2>());
Bind<IService3>().ToMethod(ctx => ServiceLocator.Get<IService3>());
第四步 - 重构不依赖于其他服务的服务
Foo{ ctor(IService1, IBar) }
Bar{ ctor(IService1, IService2, IBaz) }
Baz{ ctor(IService3) }
Service1 { ServiceLocator.Get<Service2>() }
Service2 { ServiceLocator.Get<Service3>() }
Service3 { ctor(ISomeClass) }
Bind<IBar>().To<Bar>();
Bind<IBaz>().To<Baz>();
Bind<ISomeClass>().To<SomeClass>();
Bind<IService1>().ToMethod(ctx => ServiceLocator.Get<IService1>());
Bind<IService2>().ToMethod(ctx => ServiceLocator.Get<IService2>());
Bind<IService3>().To<Service3>().InSingletonScope();
步骤5 - 接下来重构那些依赖于仅重构服务作为依赖项的服务。
Foo{ ctor(IService1, IBar) }
Bar{ ctor(IService1, IService2, IBaz) }
Baz{ ctor(IService3) }
Service1 { ServiceLocator.Get<Service2>() }
Service2 { ctor(IService3) }
Service3 { ctor(ISomeClass) }
Bind<IBar>().To<Bar>();
Bind<IBaz>().To<Baz>();
Bind<ISomeClass>().To<SomeClass>();
Bind<IService1>().ToMethod(ctx => ServiceLocator.Get<IService1>());
Bind<IService2>().To<Service2>().InSingletonScope();
Bind<IService3>().To<Service3>().InSingletonScope();
第六步 - 重复操作,直到每个服务都被重构。
Foo
Bar
Baz
Service1
Service2
Service3
Bind<IBar>().To<Bar>();
Bind<IBaz>().To<Baz>();
Bind<ISomeClass>().To<SomeClass>();
Bind<IService1>().To<Service1>().InSingletonScope();
Bind<IService2>().To<Service2>().InSingletonScope();
Bind<IService3>().To<Service3>().InSingletonScope();
也许您想与重构一起切换到基于约定的容器配置。在这种情况下,我会向所有重构类添加一个属性来标记它们,并在完成所有重构后再将其删除。在约定中,您可以使用此属性过滤所有已重构的类。