我正在尝试将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 { /* ... */ }
我也不确定是否在插件构造函数中创建了一个容器然后只是忘了它,但是让它在后台进行自动连接,是不是很好?
答案 0 :(得分:7)
理想情况下,容器使用情况应遵循Register Resolve Release pattern(RRR)。我知道你说你不能改变当前的Activator.CreateInstance用法,但它仍然有助于理解它应该如何。
如果您没有该约束,则应该只有一个容器实例,由父应用程序本身托管。然后可以使用它来组成所有插件。这将使插件能够共享依赖关系。这是MEF采用的路由,它也解决了可扩展性方案。
现在,既然你不能这样做,你可以做的下一个最好的事情是按照你的建议为每个插件设一个容器。在这一点上,它主要成为一个实现细节。在每个插件中,您仍应遵循RRR模式。
效率低下吗?除非你有很多插件并且一直在创建和销毁它们,否则几个不同的容器应该不重要。但是,测量比创建过早优化更好。
在这种情况下,您只能通过将容器设置为静态来共享容器。然而,这使事情变得比他们需要的更复杂,所以除非绝对必要,否则不要走那条路。
答案 1 :(得分:1)
我也不确定是否在插件构造函数中创建了一个容器,然后只是忘了它,但是让它在后台进行自动连接,是不是可以?
几个月后看了我自己的问题,我敢说忘记container
是不行的,因为(a)它是IDisposable
,应该这样对待,并且(b)某些组件的生命周期与容器绑定,因此容器的生命周期应该明确结束;可以使用表单的Dispose
方法,也可以触发FormClosed
事件。