WPF使用Prism6时Shell关闭前如何正确调用所有相关ViewModel的Dispose释放资源
Prism 6 WPF 应用Shell关闭前释放多模块资源解决方案
你遇到的PubSub事件无法触发的问题,90%以上的场景是因为订阅端没有持有强引用,或者发布/订阅两端使用的不是同一个IEventAggregator实例,以下是两种可直接落地的解决方案,尤其适配你的SQLTableDependency多模块监听场景:
方案1:修复PubSub事件通知逻辑(适合已有PubSub架构的项目)
注意:必须确保发布和订阅用的是同一个
IEventAggregator实例,所有实例都从DI容器解析,不要手动new,这是事件触发失败的最常见原因。
- 第一步:定义全局关闭事件
public class ApplicationShutdownEvent : PubSubEvent { } - 第二步:ViewModel端正确订阅,保留订阅引用防止被GC回收
public class YourModuleViewModel : BindableBase, IDisposable { private readonly SubscriptionToken _shutdownSubToken; private SqlTableDependency<YourTable> _sqlDependency; public YourModuleViewModel(IEventAggregator eventAggregator) { // 订阅时保留token,瞬态ViewModel需设置keepSubscriberReferenceAlive为true _shutdownSubToken = eventAggregator.GetEvent<ApplicationShutdownEvent>() .Subscribe(OnShutdown, keepSubscriberReferenceAlive: true); // 你的SQLTableDependency初始化逻辑 _sqlDependency = new SqlTableDependency<YourTable>("你的连接串"); _sqlDependency.Start(); } private void OnShutdown() { Dispose(); } public void Dispose() { _sqlDependency?.Stop(); _sqlDependency?.Dispose(); _shutdownSubToken?.Dispose(); } } - 第三步:Shell关闭事件中正确发布事件
public partial class Shell : Window { public Shell() { InitializeComponent(); Closing += OnShellClosing; } private void OnShellClosing(object sender, CancelEventArgs e) { // 从全局容器解析IEventAggregator,不要用局部实例 var eventAggregator = PrismApplicationBase.Current.Container.Resolve<IEventAggregator>(); eventAggregator.GetEvent<ApplicationShutdownEvent>().Publish(); // 等待异步释放操作完成,避免进程提前退出导致资源未释放 Task.Delay(300).Wait(); } }
方案2:全局资源托管(更推荐,完全规避PubSub失效问题)
适合多模块动态注册资源的场景,不用依赖事件通信,稳定性更高:
- 第一步:定义全局资源托管服务
public interface IAppResourceManager { void RegisterDisposable(IDisposable resource); void DisposeAll(); } public class AppResourceManager : IAppResourceManager { private readonly List<IDisposable> _resources = new List<IDisposable>(); private readonly object _lockObj = new object(); public void RegisterDisposable(IDisposable resource) { lock (_lockObj) { if (!_resources.Contains(resource)) _resources.Add(resource); } } public void DisposeAll() { lock (_lockObj) { // 倒序释放避免依赖问题 foreach (var res in _resources.AsEnumerable().Reverse()) { try { res.Dispose(); } catch { /* 按需记录释放异常 */ } } _resources.Clear(); } } } - 第二步:在App启动时将服务注册为单例
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IAppResourceManager, AppResourceManager>(); // 你的其他注册逻辑 } - 第三步:各模块初始化SQLTableDependency时注册资源
// 模块初始化类或者对应ViewModel的构造函数中 public YourModule(IAppResourceManager resManager) { var sqlDependency = new SqlTableDependency<YourTable>("你的连接串"); sqlDependency.Start(); // 注册到全局托管,不需要自己维护释放逻辑 resManager.RegisterDisposable(sqlDependency); } - 第四步:Shell关闭时统一释放所有资源
private void OnShellClosing(object sender, CancelEventArgs e) { var resManager = PrismApplicationBase.Current.Container.Resolve<IAppResourceManager>(); resManager.DisposeAll(); }
内容的提问来源于stack exchange,提问作者SiL3NC3
相关产品推荐
相关产品推荐

