推荐在Avalonia中使用Microsoft.Extensions.DependencyInjection结合AssemblyLoadContext实现插件系统:定义IPlugin等契约接口,通过隔离的AssemblyLoadContext动态加载插件DLL,插件向宿主IServiceCollection注册服务,宿主聚合INavigationProvider菜单项与IViewComponent视图并解耦通信。

在 Avalonia 中实现插件系统,推荐使用 Microsoft.Extensions.DependencyInjection(DI) 结合 MEF(Managed Extensibility Framework) 或纯 DI 方式动态加载插件。Avalonia 本身不内置插件机制,但可借助 .NET 的模块化能力(如 AssemblyLoadContext、AssemblyLoadEventArgs、接口抽象 + 运行时反射)构建松耦合、热插拔的插件架构。
定义统一插件接口与契约
所有插件必须实现约定接口,这是解耦核心。建议放在独立类库(如 MyApp.Plugins.Contracts)中供宿主和插件共同引用:
-
IPlugin:基础生命周期(
Initialize()/Shutdown()) -
INavigationProvider:提供菜单项或导航入口(返回
MenuItem或Route) -
IViewComponent:可被
ContentControl动态渲染的 Avalonia 控件(继承自Control) - 避免在接口中引用 Avalonia 程序集(如
Avalonia.Controls.Button),改用抽象类型或数据模型
插件发现与动态加载(基于 AssemblyLoadContext)
不依赖 MEF 的轻量方案(更可控、兼容 .NET 6+):
- 插件以
.dll形式存放于Plugins/目录,命名规范如MyPlugin.dll - 创建隔离的
AssemblyLoadContext防止类型冲突:
var pluginContext = new AssemblyLoadContext(isCollectible: true); var assembly = pluginContext.LoadFromAssemblyPath(pluginPath);
- 遍历
assembly.GetTypes(),筛选实现IPlugin的类型,用Activator.CreateInstance创建实例 - 调用
plugin.Initialize(services),将宿主的IServiceCollection传入,让插件注册自身服务(如services.AddSingleton)()
宿主 DI 容器集成插件服务
在 AppBuilder 构建阶段注入插件:
- 先构建基础
IServiceCollection(含 Avalonia 默认服务) - 扫描并加载插件,执行其
Initialize(IServiceCollection) - 完成所有插件注册后,调用
BuildServiceProvider() - 关键点:插件内部应只注册服务,**不调用
BuildServiceProvider**,避免容器嵌套
示例片段:
var services = new ServiceCollection();
// 注册宿主服务...
RegisterHostServices(services);
// 加载插件
foreach (var pluginPath in GetPluginPaths())
{
var plugin = LoadPlugin(pluginPath);
plugin.Initialize(services); // 插件向 services 添加自己的类型
}
var app = BuildAvaloniaApp()
.UsePlatformDetect()
.SetupWithLifetime(lifetime);
app.StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose);
运行时 UI 扩展(菜单/视图/命令)
插件通过接口向宿主“声明能力”,宿主负责聚合与呈现:
- 插件实现
INavigationProvider.GetMenuItems()→ 宿主收集所有MenuItem并添加到主菜单 - 插件实现
IViewComponent.CreateView()→ 宿主用绑定渲染 - 命令可绑定到
ICommand属性,由插件提供 ViewModel 实现,宿主仅负责触发 - 避免直接在插件中操作宿主窗口(如
Application.Current.MainWindow),改用事件或消息总线(如WeakEvent或CommunityToolkit.Mvvm.Messaging)通信
不复杂但容易忽略:确保插件 DLL 不包含重复依赖(如 Avalonia.*),全部由宿主提供;发布时将插件目录设为 CopyToOutputDirectory;调试阶段可用 AssemblyResolve 事件辅助定位加载失败原因。










