Prism技术问题:如何仅当DLL存在时才加载模块
问题描述
我正在使用Prism和Unity开发WPF应用程序,需要实现一个可选模块的加载逻辑:
- 无该模块DLL时程序能正常启动
- 把模块DLL复制到输出目录并重启后,模块能自动加载
当前使用的XamlModuleCatalog配置如下:
<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Modularity="clr-namespace:Prism.Modularity;assembly=Prism.Wpf"> <Modularity:ModuleInfo Ref="file://TrayManager.UI.dll" ModuleName="UIModule" ModuleType="TrayManager.UI.UIModule, TrayManager.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Modularity:ModuleInfo Ref="file://TrayManager.PasswordEncryption.dll" InitializationMode="OnDemand" ModuleName="PasswordEncryptionModule" ModuleType="TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </Modularity:ModuleCatalog>
遇到的问题:
- 把
PasswordEncryptionModule的InitializationMode设为OnDemand时,程序能正常启动,但模块永远不会被加载 - 设为
WhenAvailable时,只有当DLL在输出目录中时程序才能启动
App.xaml.cs代码如下:
protected override Window CreateShell() { _logger.Debug("Shell created"); return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override IModuleCatalog CreateModuleCatalog() { return new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative)); }
请问哪里操作出错了?
解决方案
核心问题分析
你的配置存在两个关键问题:
WhenAvailable模式的强制检查:当设置该模式时,Prism启动阶段会验证模块DLL是否存在,不存在则直接抛出异常导致启动失败。OnDemand模式的被动性:该模式下Prism不会自动加载模块,必须手动触发加载操作;同时静态XAML配置的模块,即使DLL后来被添加,Prism也不会主动检测。
方案一:启动时动态检测并注册可选模块
修改CreateModuleCatalog方法,先加载XAML中的固定模块,再手动检查可选模块DLL是否存在,存在则添加到模块目录并设置自动加载:
protected override IModuleCatalog CreateModuleCatalog() { // 加载XAML中定义的基础模块 var catalog = new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative)); // 检查可选模块DLL是否存在于程序目录 var moduleDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TrayManager.PasswordEncryption.dll"); if (File.Exists(moduleDllPath)) { catalog.AddModule(new ModuleInfo { ModuleName = "PasswordEncryptionModule", ModuleType = "TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", Ref = $"file://{moduleDllPath}", InitializationMode = InitializationMode.WhenAvailable }); } return catalog; }
这个方案的优势:
- 无DLL时程序正常启动,不会加载该模块
- 复制DLL到目录后重启,程序自动检测并加载模块
方案二:使用目录扫描自动发现模块
如果需要支持多个可选模块,可结合DirectoryModuleCatalog扫描程序目录自动发现模块:
protected override IModuleCatalog CreateModuleCatalog() { // 加载XAML中的固定模块 var xamlCatalog = new XamlModuleCatalog(new Uri("/TrayManager;component/ModuleCatalog.xaml", UriKind.Relative)); // 创建目录扫描目录,扫描当前程序目录下的模块 var directoryCatalog = new DirectoryModuleCatalog { ModulePath = AppDomain.CurrentDomain.BaseDirectory }; // 合并两个模块目录 var aggregateCatalog = new AggregateModuleCatalog(); aggregateCatalog.AddCatalog(xamlCatalog); aggregateCatalog.AddCatalog(directoryCatalog); return aggregateCatalog; }
需要给可选模块类添加特性声明,让Prism扫描时识别:
[Module(ModuleName = "PasswordEncryptionModule", InitializationMode = InitializationMode.WhenAvailable)] public class PasswordEncryptionModule : IModule { // 模块初始化逻辑 public void Initialize() { // ... } }
方案三:保留XAML配置并手动触发加载(不推荐)
如果坚持用XAML配置,可给可选模块添加Required="False"属性,避免无DLL时启动失败,然后手动触发加载:
修改XAML配置:
<Modularity:ModuleInfo Ref="file://TrayManager.PasswordEncryption.dll" InitializationMode="OnDemand" Required="False" ModuleName="PasswordEncryptionModule" ModuleType="TrayManager.PasswordEncryption.PasswordEncryptionModule, TrayManager.PasswordEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
在Shell初始化后手动加载模块:
protected override void InitializeShell() { base.InitializeShell(); var moduleManager = Container.Resolve<IModuleManager>(); var moduleCatalog = Container.Resolve<IModuleCatalog>(); var targetModule = moduleCatalog.Modules.FirstOrDefault(m => m.ModuleName == "PasswordEncryptionModule"); if (targetModule != null && !targetModule.IsLoaded) { moduleManager.LoadModule("PasswordEncryptionModule"); } }
内容的提问来源于stack exchange,提问作者N0m4n904
相关产品推荐
相关产品推荐

