使用 DI 创建具有上下文的插件实例

本文关键字:插件 实例 上下文 DI 创建 使用 | 更新日期: 2023-09-27 18:03:54

我正在重构我们的应用程序以包含依赖注入(通过构造函数注入(,并遇到了一个棘手的极端情况:

我们目前有ImageViewer对象,当实例化时,它们在程序集中搜索ImageViewerPlugin(抽象基类(实例,并使用反射实例化它们。这是在ImageViewer的构造函数中使用类似于以下内容的方法(在所有具体插件类型的循环中调用(完成

的:
private ImageViewerPlugin LoadPlugin(Type concretePluginType)
{
  var pluginConstructor = concretePluginType.GetConstructor(
    BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public,
    null,
    new[] { typeof(ImageViewer) },
    null);
  return (ImageViewerPlugin) pluginConstructor.Invoke(
    new object[] { constructorParameter });
}

ImageViewerPlugin 类大致如下所示:

internal ImageViewerPlugin
{
  protected ImageViewer _viewer;
  protected ImageViewerPlugin(ImageViewer viewer)
  {
    _viewer = viewer;
  }
}

具体实现大致如下所示:

internal AnImageViewerPlugin
{
  public AnImageViewerPlugin(ImageViewer viewer) : base(viewer)
  {
  }
}

每个ImageViewer实例都有自己的ImageViewerPlugin实例集合。

现在,应用程序正在重构为使用 DI 容器和构造函数注入,我发现这些插件具有需要由 DI 容器解决的依赖项(以前通过使用全局静态类隐藏(,但我不确定如何在不使用服务定位器(反模式(的情况下做到这一点。

最明智的解决方案似乎是使用 DI 创建这些插件实例。这将允许我添加额外的构造函数参数,以便通过构造函数注入注入它们所依赖的依赖项。但是,如果我这样做,如何在注入其余参数值的同时传递特定的viewer参数值?

我认为ImageViewerPluginFactory将有助于实现这一目标,但看不到如何实现这样的工厂,因为每个插件可能具有不同的构造函数签名。

如何解决这种情况?还是我完全错误地接近了这一点?

使用 DI 创建具有上下文的插件实例

因此,

您有一个依赖于ImageViewerPlugin实例集合的ImageViewer,每个实例都依赖于n个ImageViewer依赖于ImageViewerPlugin实例的集合,每个实例都依赖于n个ImageViewer依赖于ImageViewerPlugin实例的集合,每个实例都依赖于...好吧,你明白了:-(

这是一个循环引用。撇开整个 DI 容器的事情不谈,在手动执行此操作时如何创建此层次结构?使用构造函数注入,您不能。从以下示例可以看出:

var plugin = new ImageViewerPlugin( [what goes here?] );
var viewer = new ImageViewer(plugin);

你将不得不以某种方式打破这个依赖循环,在这种情况下,一般建议是依靠属性注入:

var plugin = new ImageViewerPlugin();
var viewer = new ImageViewer(plugin);
// Property injection
plugin.Viewer = viewer;

但更重要的是,您应该仔细查看应用程序的设计,因为循环引用通常表示设计中存在问题。例如,仔细查看插件需要查看器的行为以及查看器需要插件的行为。您也许能够将其提取到另一个类中,如下所示:

var imageServices = new ImageServices();
var plugin = new ImageViewerPlugin(imageServices);
var viewer = new ImageViewer(imageServices, plugin);

这完全解决了问题,但这是否可行取决于您的情况。

使用最后一个解决方案,使用简单注射器注册将相当简单。使用属性注入破坏依赖关系时,可以使用 RegisterInitializer(Action( 方法。它允许您打破依赖循环。例如:

container.RegisterInitializer<ImageViewer>(viewer =>
{
    foreach (var plugin in viewer.Plugins)
    {
        plugin.Viewer = viewer;
    }
});