我在这里已经阅读了其他各种问题,最值得注意的是
Dependency Inject (DI) “friendly” library
Ioc/DI - Why do I have to reference all layers/assemblies in entry application?
和article(和其他各种材料)。
但是我不清楚将组合根放在库(DLL).NET项目中的哪个位置。该项目不属于本文中提到的任何特定类型。在桌面,控制台甚至Web应用程序中,这一点都是明确定义的。
我目前的方法是包装容器,注册类型并重新公开Resolve方法:
class DefaultBootstrapper : IBootstrapper {
public Bootstrapper() {
_container = new XXXContainer();
RegisterTypes(_container);
}
public T Resolve<T>() where T : class {
return _container.Resolve<T>();
}
// + other _container.Resolve() overloads
private readonly XXXContainer _container;
}
然后我阻止库消费者创建库的根实例(例如定义内部构造函数),从而强制使用单例工厂:
class XYZFactory {
static XYZFactory() {}
private XYZFactory(IBootstrapper bootstrapper) {
_bootstrapper = bootstrapper;
}
public static XYZFactory Instance {
get { return Singleton; }
}
public ABCType CreateABCType(string param1) {
return _bootstrapper.Resolve<ABCType>(param1, _bootstrapper.Resolve<Dependency1>);
}
private static readonly XYZFactory Singleton = XYZFactory(new DefaultBootstrapper);
private readonly IBootstrapper _bootstrapper;
}
问题是,在库项目中找到组合根有更好的方法或更好的模式吗?
答案 0 :(得分:13)
这取决于您正在创建的库的类型。您的库项目是您自己的解决方案的一部分,还是其他开发人员在您的团队,部门甚至组织之外依赖的可重用库?
如果它只是解决方案的库项目的一部分,那么库项目本身通常不应包含组合根。根据定义,composition root是应用程序中“(最好)唯一的位置,其中模块组合在一起”。换句话说,您的解决方案将具有一个或多个启动项目(例如MVC应用程序,WCF服务,控制台应用程序),并且每个启动项目将获得其自己的组合根。下面的图层不会得到他们自己的作品根。
这个btw并不意味着你不应该阻止组合根目录中的代码重复。当包含项目(例如DAL和BLL)的默认连线导致大量重复时,通常应将此逻辑提取到另一个项目。您可以通过在其中一个项目(很可能是BLL)中包含部分注册逻辑来执行此操作,并让每个组合根调用该共享逻辑,或者您可以通过为该项目添加特殊的“引导程序”项目来完成此操作,引用的项目。此引导程序项目仅包含注册逻辑。通过将此逻辑与应用程序程序集分开,可以防止这些程序集需要依赖于使用的依赖项注入库。但是,如果程序集依赖于此类库,通常不会出现问题,只要您确保应用程序逻辑不会依赖于容器。
对于可重用的库,事情通常是不同的。在这种情况下,消费者将使用您的库,但您无法控制他们如何构建其应用程序。您经常希望以一种可以直接被消费者使用的方式提供库,而无需在其组合根目录中进行各种“复杂”注册。你甚至根本不知道他们是否有成分根。
在这种情况下,您通常应该使您的库在没有DI容器的情况下工作。您自己不应该依赖于这样的容器,因为这会将容器拖入。如果您使用容器,请问自己为什么可重用库使用容器,如果必须这样。也许你这样做是因为你围绕依赖注入原则设计了所有类型;因为这使测试更容易。不要忘记这是你的问题,而不是你的消费者的问题。作为可重复使用的库设计师,您应该尽可能地让您的库尽可能地为您的消费者使用。请不要假设您的消费者正在使用DI容器。即使他们练习依赖注入,他们也可以应用Pure DI而不是DI容器。
如果您正在构建可重复使用的库,请查看Mark Seemann的this blog帖子。