使用 WPF/Prism 在应用程序退出/崩溃时释放对象实例



我正在通过DryIoc将棱镜框架用于我的WPF应用程序,但也许我不太了解它的某些用途。(自学成才)或最佳实践。

我有一个类库(MyLibrary),它遵循一次性模式,因为它需要做一些清理。 我有一个在其视图模型中使用MyLibrary的模块。

如果应用程序关闭甚至应用程序崩溃,我想在此库上调用 dispose,以防万一此时尚未正确处理(否则它将使第三方应用程序在后台打开)

目前只有 1 个实例,但将来可能会超过 1 个。

到目前为止,这就是我想出的:

当我的视图模型第一次需要MyLibrary时,它将注册一个实例,如下所示:

this.ce.RegisterInstance<MyLibrary>(this.myLibrary, "MyLibraryName");ce在哪里Prism.Ioc.IContainerExtension然后在我的 App.xaml 中的protected override void OnExit(ExitEventArgs e)方法中.cs我将执行以下操作:this.Container.Resolve<MyLibrary>("MyLibraryName")?.Dispose();

这成功地处置了我的库对象(并按照我的预期关闭了第 3 方窗口),但是,这对我来说似乎有点不对劲(代码气味?

有没有更好的方法?例如,我不觉得 App.xaml.cs 应该知道我的模块的视图模型想出的实例名称。有没有办法只对容器中的所有MyLibrary类型调用 Dispose 而不使用名称字符串?

我希望我可以遍历容器中的所有MyLibrary并调用 Dispose? 如果没有更好的方法,那么我想我可以接受。

但同样重要的是:我如何尝试以类似的方式调用 Dispose 处理任何未经处理的异常,否则会导致应用程序崩溃?我可以以某种方式访问容器并处理任何MyLibrary吗? 我不打算尝试保存应用程序免于崩溃,只需确保MyLibrary调用其 Dispose 方法即可。

>注释响应(字符过多):

单击 UI 按钮>委托命令>在 VM 中用方法编写的代码。 库具有启动(登录)的开销。

UI 上有多个需要使用库的输入步骤。 在每个阶段(按钮单击)将其包装在using中并非不可能,但由于库启动开销,效率低下。

我想:

- Instantiate library
- User inputs data, library does work, returns results, requires more input etc
- Several more of these data input stages / library work / results
- Dispose

问题:

- User exits application before library object is disposed
- How/Where to dispose?

尝试的解决方案:

- Use the container so that i can dispose in App.xaml.cs `OnExit`
- This solution works, but feels incorrect?

问题:在这种情况下,我可以做得更好/不同吗? 附加问题:在棱镜应用程序中发生致命崩溃时如何处理此问题?

重要的是,它被处理掉,这样库使用的第三方应用程序的实例就不会在后台运行。

好的,我现在将其更改为在app.xaml中注册一个实例.cs我可以避免.Resolve调用。你会这样做吗?如果不是,那怎么办?当它被注册为单例时,我不知道如何在没有.Resolve的情况下将其OnExit

我的 app.xaml 的削减/简化示例.cs现在:

namespace MyNamespace
{
public partial class App
{
public App()
{
// Crash handling
this.Dispatcher.UnhandledException += this.OnDispatcherUnhandledException;
}

// My library provider that contains a dictionary of library instances.
public IMyProvider MyProvider { get; set; } = new MyProvider();
// Dispose provider on crash
public void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
this.MyProvider.Dispose();
MessageBox.Show("Unhandled exception occurred: n" + e.Exception.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
protected override Window CreateShell()
{
return this.Container.Resolve<MainWindow>();
}
// Register as an instance?
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IMyProvider>(this.MyProvider);
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
// Add modules here...
}
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
// Dispose my provider (and all contained instances in its dictionary)
this.MyProvider.Dispose();
}
}
}

注释字符限制阻止我再次写太多。

@Haukinger: tl;大卫:你让我走上了正确的轨道。

我没有完全按照你的例子(不使用 Lazy,库没有接口),但你让我走上了正确的轨道。 总的来说,我正在努力理解:DI,IoC,接口以及它们之间的关系。

你关于滥用容器的台词让我仔细观察了一下。 首先,我从视图模型中删除了所有containerExtension.resolve实例,而是像往常一样注入到构造函数中。

我创建了一个类似于您建议的提供程序,并为我的库创建了一个提供程序接口。 提供程序实现保留命名库实例的字典,以防我需要多个实例。 我仍然在应用程序的OnExit中调用this.Container.Resolve<IMyProvider>().Dispose();,但它感觉更好,因为我不再需要命名实例。 不过,仅仅拥有一个提供程序接口仍然不能阻止任何人自己新建库实例?

库接口注册如下:containerRegistry.RegisterSingleton<IMyProvider, MyProvider>();

除了我的图书馆: 我目前正在为容器中的应用程序主题等一些内容注册一些应用程序范围的实例,如下所示:containerRegistry.RegisterInstance<MyThing>(this.MyThing);这里仍然没有使用接口。仍在尝试理解它们,但是容器中主题的应用程序范围实例工作正常。

从我看到的IAnimal接口可以具有DogCat实现的示例中,我的对象没有多个实现要求。(除了我尚未尝试学习的单元测试,我认为在这种情况下我将从接口中受益)我只需要从不同的模块/程序集访问一些东西,并使用容器来做到这一点。

我最大的问题之一是我有参数来构造一些实例,例如我的库,并且这些参数在运行时之前是未知的,因此当应用程序启动时.cs我无法在 app.xaml 中注册实例。

如果我必须使用仅在运行时已知的参数(例如凭据)构造某些内容,我是否需要遵循一种带有接口的工厂模式才能注册它们,或者我缺少某些内容? 我想我可以在创建实例时使用容器扩展注册一个实例(在程序集 A 的视图模型 A 中)?但是如果我想将它们放在不同的模块(程序集 B 的视图模型 X)中,我稍后需要使用.Resolve? 这感觉不对,为简单的凭据对象创建工厂和工厂接口感觉矫枉过正?

我想我只是没有完全理解接口,因为我看到的示例通常没有实现参数,或者当我只有 1 个实现时,它们专注于讨论不同实现的好处。 或者示例与 IoC 或 DI 无关。

就处理应用程序崩溃事件时的处置而言,我做了以下操作:

public App()
{
this.Dispatcher.UnhandledException += this.OnDispatcherUnhandledException;
}
public void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
// Dispose provider
this.Container.Resolve<IMyProvider>().Dispose();
// Unhandled exception messagebox:
MessageBox.Show("Unhandled exception occurred: n" + e.Exception.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}

我仍然在为接口/工厂模式/DI/Ioc/一般设计而烦恼,但你让我走上了正确的轨道。 我正在更好地将东西注入构造函数中,我可以使用提供程序管理我的库实例,并在退出和崩溃时处理

。谢谢。

我建议你不要在这里滥用容器。

相反,自己为您的库创建一个管理器,并在关闭时清理该管理器:

public interface ILibraryProvider : IDisposable
{
ILibrary GetLibrary();
}
internal class LibraryProvider : ILibraryProvider
{
public LibraryProvider( Lazy<ILibrary> library )
{
_library = library;
}
#region ILibraryProvider
public ILibrary GetLibrary() => _library.Value;
public void Dispose()
{
if (_library.IsValueCreated)
_library.Value.Dispose();
}
#endregion
#region private
private readonly Lazy<ILibrary> _library;
#endregion
}

Application.OnExit,处理LibraryManager

如果需要多个不同的库,请在Get中添加字符串参数,注入Func而不是Lazy并将实例存储在Dictionary中。

最新更新