找到.NET库的正确组合根 [英] Locate the correct composition root for a .NET library

查看:118
本文介绍了找到.NET库的正确组合根的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在这里讨论了其他一些问题,其中最着名的是

I've read various other question here on the argument, most notably

依赖注入(DI)友好库

Ioc / DI - 为什么我必须引用所有的层/组件应用程序?

和此文章(和其他各种材料)。

and this article (and other various material).

然而,我不清楚将组成根放在库(DLL).NET项目中。该项目不属于文章中提到的任何特定类型。在桌面,控制台或甚至Web应用程序中,这一点相反地明确定义。

However it's not clear to me where to place composition root in library (DLL) .NET project. The project does not belong to any specific type mentioned in the article. In desktop, console or even web application this point is instead clearly defined.

我目前的方法是包装容器,注册类型并重新公开Resolve方法: / p>

My current approach is to wrap the container, register types and re-expose the Resolve method:

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;
}

然后我阻止图书馆消费者创建根实例 (例如定义内部构造函数),因此强制使用单身工厂:

Then I prevent library consumers to create root instances of the library (e.g. defining internal constructors) and hence forcing the use of a singleton factory:

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;
}

问题是,有更好的方法或更好的模式用于定位图书馆项目中的构图根

The question is, there's a better approach or a better pattern to employ for locate the composition root in a library project?

推荐答案

这取决于您正在创建的库的类型。您的图书馆项目是您自己的解决方案的一部分,还是其他开发人员在您的团队,部门或甚至组织之外的可重用库?

It depends on the type of library you are creating. Is your library project part of your own solution, or is it a reusable library that other developers depend upon outside your team, department, or perhaps even organization?

在这种情况下它只是解决方案的库项目部分,库项目本身通常不包含组合根。根据定义,组合根是(最好)唯一的位置在模块组合在一起的应用程序中。换句话说,您的解决方案将有一个或多个启动项目(如MVC应用程序,WCF服务,控制台应用程序),并且每个启动项目将获得自己的组合根。下面的层不会得到自己的组合根。

In the case of it being just a library project part of a solution, the library project should itself typically not contain a composition root. By definition, the composition root is a "(preferably) unique location in an application where modules are composed together". In other words, your solution would have one or multiple start-up projects (such as a MVC application, WCF service, console app), and each start-up project would get its own composition root. Layers below would not get their own composition root.

这个btw并不意味着你不应该防止组合根中的代码重复。当由于包含项目(如DAL和BLL)的默认布线引起大量重复时,您通常应将此逻辑提取到另一个项目。您可以通过将一部分注册逻辑包含在其中一个项目(很可能是BLL)中,并使每个组合根调用该共享逻辑,或者通过为该项目添加一个特殊的引导程序项目来实现,参考项目。这个引导程序项目只会容纳注册逻辑。通过将此逻辑与应用程序程序集分离,可以防止这些程序集需要依赖于依赖注入框架。但是,如果程序集依赖于这种框架,那么通常不会有问题,只要您确保应用程序逻辑不会对容器采取依赖。

This btw does not mean that you should not prevent code duplication inside the composition roots. When there is a lot of duplication caused by a default wiring for included projects (such as DAL and BLL), you should typically extract this logic to another project. You can either do this by including part of the registration logic inside one of the projects (most likely the BLL) and let each composition root call that shared logic, or you can do this by adding a special 'bootstrapper' project for that project and the referenced projects. This bootstrapper project will only container the registration logic. By separating this logic from the application assemblies you prevent those assemblies from needing a dependency on the used dependency injection framework. It is however usually not a problem if a assembly takes a dependency on such framework, as long as you make sure the application logic keeps free from taking dependencies on the container.

对于可重用的框架,通常情况是不同的。在这种情况下,消费者将使用您的图书馆,但您无法控制他们的应用程序的结构。您经常希望以一种可以直接由消费者消费的方式提供图书馆,而不必在其组成根目录中进行各种复杂注册。你通常甚至不知道他们是否有一个组成根。

For reusable frameworks things are usually different. In that case consumers will use your library, but you have no control over how they structure their application. You often want to supply the library in a way that it can directly be consumed by consumers, without having to do all kinds of 'complex' registration in their composition root. You often don't even know if they have a composition root at all.

在这种情况下,你应该通常使你的库工作没有DI容器。你应该不要依赖这样一个容器,因为这将拖动容器。如果你使用一个容器,问自己为什么你的可重用库使用一个容器,如果这是必须的。也许你这样做是因为你设计了依赖注入原理的所有类型;因为这使得测试更容易。不要忘记这是你的问题,而不是你的消费者的问题。作为框架设计师,您应该尽可能地为您的消费者尽可能使用您的框架。

In that case you should typically make your library working without a DI container. You should yourself not take a dependency on such a container, because this would drag the container in. If you do use a container, question yourself why your reusable library uses a container, and if this has to be. Perhaps you do because you designed all types around the dependency injection principle; because this makes testing easier. Don't forget that this is your problem, not the problem of your consumers. As a framework designer, you should hard in getting your framework as usable as possible for your consumers.

有几种技巧可以帮助您。例如,定义一个公共默认构造函数,该构造函数调用一个内部构造函数,该构造函数接受所有的依赖关系。这是一种称为穷人注射的模式,通常是反模式。当在LOB应用程序中使用时,它是一种反模式。但是正如我所解释的那样,可重用的库是不同的,因为你不控制该代码的用户,但是你必须确保版本之间的向后兼容性。拥有依赖关系的内部构造函数允许您的单元测试调用该构造函数(通过使用 InternalsVisibleToAttribute )。

There are several 'tricks' that can help you here. For instance, define a public default constructor that calls into an internal constructor that takes all dependencies. This is a pattern called 'poor-mans injection' and is generally an anti-pattern. It's an anti-pattern when used in a LOB application. But as I explained, reusable libraries are different since you don't control the user of that code, but you'll have to ensure backwards compatibility between versions. Having an internal constructor that takes the dependencies allows your unit tests to call that constructor (by making use of the InternalsVisibleToAttribute).

另一个窍门是使用工厂。您定义工厂界面和内部工厂实现。这个实现可以调用这些类型的内部构造函数。

如果您的可重用项目必须依赖于DI框架,请尝试提取接线逻辑并提供额外的引导程序组件。这允许消费者使用自己的DI框架,而不必参考您为图书馆选择的DI框架。

如果您正在构建一个可重用的库,请查看Mark Seemann的此博客帖子。

In case you are building a reusable library, take a look at this blog post from Mark Seemann.

这篇关于找到.NET库的正确组合根的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆