如何在不强迫用户使用库的IOC容器的情况下编写库 [英] how to write libraries without forcing users to use the library's IOC container

查看:62
本文介绍了如何在不强迫用户使用库的IOC容器的情况下编写库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简短的问题是:
给定库 warrants ,该库使用内部特定的IOC容器,当应用程序使用该库时,给定应用程序 warrants 使用一个IOC容器连接其依赖项,如果两个容器不同,那么它们如何一起玩呢?

The short question is: Given a library warrants using a particular IOC container for its internals, when an application consumes that library, given the app warrants using an IOC container for wiring its dependencies, given if the the two containers are different, how can they play well together?

这种情况是,应用程序定义了一些类,取决于库中的类型。因此,当应用程序容器尝试构建此类时,它需要知道如何解析存在于库中的类型。

The scenario is, the application has classes defined that depend on types from the library. So when the application container attempts to build such a class, it needs to know how to resolve the type that lives in the library.

这是一个long绕的问题:

Here's the long winded question:

这个问题以前似乎确实以不同的形式和形式被问到在SO上,但我似乎找不到所需的答案,因此我将通过一个假设的_over_simplified_具体示例进行探讨。
我们想编写一个用于记录的库,用户可以将其作为软件包包含在解决方案中,以立即使用记录功能。
库公开的公共接口。.

This question does seem to have been asked in different shapes and form before on SO, but I can't seem to find the answer I need so I am going to have a go at it with a hypothetical _over_simplified_ concrete example. We want to write a library for logging that users can include as a package in their solution to get logging functionality out of the box. The public interfaces the library exposes are..

公共接口ILogger {}

公共接口ITarget {}

具体实现是

内部类Logger:ILogger {public Logger(ITarget target){}}

内部类FileTarget:ITarget {}

要求是用户是否包括我们的软件包并定义具有属性 ILogger 的类或具有ctor参数类型为 ILogger 的类,则我们的库负责注入该接口到用户定义类的具体实现。默认情况下,注入的记录器将进入文件系统进行记录,因为 ITarget 的默认实现已注入 ILogger 实现是我们库中的 FileTarget

Requirements are if the user includes our package and defines a class with a property of type ILogger or has a ctor argument of type ILogger then our library is responsible for injecting a concrete implementation for that interface into the user defined class. By default that injected logger will go to the file system for logging because the default implementation of an ITarget injected into the ILogger implementation is a FileTarget by our library.

如果用户决定编写实现<$ c $的类c> ITarget 接口,然后我们的库将使用该接口将其注入 Logger 类,而不使用其默认的 FileTarget 实现。

If the user decides to write a class implementing the ITarget interface then our library will use that to inject into the Logger class and not use its default FileTarget implementation.

我想证明的是,它们是双向依赖。

SO what I wish to demonstrate, is their is a bi-directional dependency here.


  1. 我们的库取决于用户的程序集,因为它需要扫描用户的程序集以加载任何扩展点(即 ITarget 实施),然后在任何默认实现之前将其注入到自己的对象中。

  1. Our library depends on the user's assemblies, since it needs to scan the user's assemblies to load any extension points (i.e. an ITarget implementation) and inject those into its own objects ahead of any default implementations.

用户的程序集取决于在库中,由于如果用户选择使用 ILogger 接口定义一个类作为依赖项,则该用户对象应获得对运行时提供的该接口的具体引用,我们的图书馆。

The user's assemblies depends on the library, since if the user chooses to define a class with an ILogger interface as a dependency, then that user object should get a concrete reference to that interface provided at runtime by our library.

简单的解决方案是,如果用户和我们的图书馆都使用同一个IOC容器,则会出现问题解决了。但这是一个强有力的假设。我想做的是

The easy solution is if the user and our library are both using the same IOC container, then problem is solved. But this is a strong assumption. What I wish to do is


  1. 在库中使用IOC容器,以最好地满足库的需求,在我的情况下是Ninject。 / li>
  2. 在运行时以某种方式为用户提供了一种机制,以使其可以通过某些API调用我的库,这将确保Ninject被触发并扫描用户的程序集,并在考虑所有扩展名的情况下进行连线

到目前为止,它可以很好地实现,但这是棘手的部分。

So far so good, its perfectly achievable, but here comes the tricky part.


  • 如果用户也使用Ninject,则问题将自动解决,因为Ninject已经知道如何解决我们库中的接口。但是,如果用户决定使用自己选择的IOC容器怎么办?

我几乎想用类似这样的接口在库中定义某种子容器功能:

I almost want to define some sort of child container functionality in the library with an interface like such

公共接口IDependencyResolvingModule {T Get&T;(); [] T GetAll< T>(); }

并提供一个实现,该实现使用我们的库选择的容器(即Ninect)来解析上述两种方法所请求的类型。

and provide an implementation that uses our library's choice of container (i.e. Ninect) to resolve the type requested in the two methods define above.

我希望用户的IOC容器具有一些无法解析依赖项的功能(即 ILogger ),它应该挂接到 IDependencyResolvingModule 实现并请求依赖项。

I want the user's IOC container to have some functionality where if it can't resolve a dependency (i.e. an ILogger), it should hook into the IDependencyResolvingModule implementation and ask for the dependency.

这样,我们的库就可以使用其选择的IOC容器,用户代码可以解决其IOC容器不了解的依赖项。如果IOC容器在那里提供一些功能,以注册在执行程序集目录中的程序集中找到的任何 IDependencyResolverModules 的单例实例以及何时无法解析,该解决方案将行不通

This way our library gets to use its choice of IOC Container, and the user's code has a way to resolve dependencies that its IOC container has no clue about. Wouldn't this solution work if IOC containers out there some how provided functionality to register singleton instances of any IDependencyResolverModules found in assemblies in the executing assembly's dir and when they can't resolve a type, ask any of the singleton modules?

但是如果没有一个需要其他IOC容器容纳的解决方案,那么还有其他解决方案吗?因此,几行中的问题是,当第三方程序集选择使用IOC容器作为其内部组件时,这是一个简单的解决方案,以使该库可以简单地为坐在外面的IOC容器提供一种机制,以挂钩并解决依赖关系住在图书馆里。

But barring a solution that requires every other IOC container to accommodate, how else can this be solved? SO the problem in a few lines is, when a third party assembly chooses to use an IOC container for its internals, what is an easy solution such that this library can simply provide a mechanism for an IOC container sitting outside to hook into and resolve dependencies that live in the library.

推荐答案

我在这里看到了几种可能的方法:

I see few possible approaches here:


  1. 为所有流行的IoC容器编写默认注册器。它们每个都应放在单独的组件中。然后,开发人员可以选择所需的容器,并为其配置容器。

  1. Write default registrator for all of the popular IoC containers. Each of them should be placed in the separate assembly. Then developer can choose the one he needs and configure his container with it.

定义您自己的工厂抽象并编写默认实现,该实现将返回默认记录器。让开发人员代替该工厂的实施。例如,为他最喜欢的容器使用适配器。这种方法与容器无关,因为开发人员只能使用默认的工厂实现。但是这种方式与自动接线无关。

Define your own factory abstraction and write default implementation that will return the default logger. Let developer to substitute implementation of that factory. For example, with adapter for his favorite container. This approach is most container-agnostic, because developer can just use the default factory implementation. But this way has nothing to do with auto-wiring.

第一种方法的惰性变体。编写有关配置容器以使用默认实现的小型手册。然后,开发人员可以自己配置容器。

The lazy variation of the first approach. Write small manual about configuring a container to work with default implementations. Then developer could configure the container himself.

组合所有以前的解决方案,以满足每个开发人员的需求。 :)

Combine all previous solutions to satisfy every developer. :)

编辑:添加了两个容器集成的示例

added example of integration of two containers

var allPublicInterfacesFromLibrary = typeof(AnyLibraryType)
    .Assembly.GetTypes()
    .Where(t => t.IsInterface && t.IsPublic);

foreach (var libraryInterface in allPublicInterfacesFromLibrary)
{
    var local = libraryInterface; //to prevent closure
    applicationContainer.Register(
        Component.For(libraryInterface)
        //delegate resolving
        .UsingFactoryMethod(k => libraryContainer.Resolve(local)) 
        //delegate lifetime management
        .LifestyleTransient() 
    );
}

这篇关于如何在不强迫用户使用库的IOC容器的情况下编写库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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