【问题标题】:DI (Autofac) in a plugin architecture: Is one separate DI container per plug-in OK?插件架构中的 DI (Autofac):每个插件有一个单独的 DI 容器可以吗?
【发布时间】:2010-10-15 08:35:06
【问题描述】:

我正在尝试将 DI(使用 Autofac)引入现有的 Windows 窗体应用程序。

这个应用程序有一个基本的插件架构,每个插件都显示自己的表单。启动时,应用程序会扫描已注册的程序集以查找实现 IPlugin 的类型,然后使用 Activator.CreateInstance 激活这些程序集:

public interface IPlugin
{
    Form MainForm { get; }
}

不能改变这个给定的框架。这意味着,每个插件类都是通过非 DI 方式实例化的,在我看来,因此我必须为每个插件引导一个单独的 DI 容器。

我的问题是,是否可以为每个插件创建一个单独的 ContainerBuilder 和容器并且仍然相当有效? (大约有 10 个不同的插件。)或者整个应用程序应该只有一个 DI 容器?

我在下面提供了我当前解决方案的一些示例代码。


using Autofac;
using System.Windows.Forms;

public class Plugin : IPlugin  // instantiated by Activator
{
    public Form MainForm { get; private set; }

    public Plugin()  // parameter-less constructor required by plugin framework
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule(new Configuration());
        var container = builder.Build();

        MainForm = container.Resolve<MainForm>();
        // ^ preferred to new MainForm(...) because this way, I can take
        //   advantage of having dependencies auto-wired by the container.
    }
}

internal class Configuration : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<MainForm>().SingleInstance();
        // ... more plugin-specific registrations go here...
    }
}

internal class MainForm : Form { /* ... */ }

我也不确定是否在插件构造函数中创建一个容器然后简单地忘记它,而是让它在后台进行自动装配,可以吗?

【问题讨论】:

    标签: c# dependency-injection autofac add-in


    【解决方案1】:

    您的容器使用最好遵循Register Resolve Release pattern (RRR)。我知道您说过您无法更改当前的 Activator.CreateInstance 用法,但了解它的真正用途仍然会有所帮助。

    如果您没有该限制,则应该只有一个容器实例,由父应用程序本身托管。然后可以使用它来组合所有插件。这将使插件能够共享依赖项。这是 MEF 采取的路线,它也解决了可扩展性场景。

    现在,既然您不能这样做,那么您可以做的最好的事情就是按照您的建议为每个插件创建一个容器。那时它主要成为实现细节。在每个插件中,您仍然应该遵循 RRR 模式。

    会不会效率低下?除非您有很多插件并且一直创建和销毁它们,否则几个不同的容器应该无关紧要。但是,与其过早地进行优化,不如进行衡量。

    在这种情况下,您只能通过将容器设为静态来共享容器。但是,这会使事情变得比实际需要的更复杂,所以除非绝对必要,否则不要走那条路。

    【讨论】:

    • 感谢您的回复,@Mark。如果我理解正确,我的示例代码已经遵循 RRR 模式......对吗? (除了我不释放容器,因为它需要至少与根组件 MainForm 一样长。我想,理想的方法是制作插件 IDisposable 并释放Dispose 方法中的容器。)
    • 您的代码中没有任何内容表明您不遵循 RRR,但很难说。在任何情况下,请记住 SingleInstance 生活方式只定义了一个容器范围的 Singleton。不是真正的Singleton,所以不能这样共享MainForm。
    【解决方案2】:

    我也不确定是否在插件构造函数中创建一个容器然后简单地忘记它,而是让它在后台进行自动装配,可以吗?

    几个月后再看我自己的问题,我敢说只是忘记 container 是不行的,因为 (a) 它是 IDisposable 并且应该这样对待,以及 (b) 某些组件的生命周期绑定到容器的,因此容器的生命周期应该明确结束;无论是在表单的Dispose 方法中还是在其FormClosed 事件被触发时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-30
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 2020-06-09
      • 1970-01-01
      相关资源
      最近更新 更多