验证时如何清理 SimpleInjector 创建的实例?

Bev*_*van 5 c# wpf simple-injector .net-core

作为创建我的 SimpleInjector 容器的一部分,我遵循了推荐的做法并调用container.Verify()以检查我的类型注册是否有意义。

这工作得很好,并且发现了我犯的许多错误 - 但它也会产生我想清理的碎片。

我的一个类是单例事件中心,用于在其他瞬态组件之间路由消息;这些其他组件在其构造函数中接受事件中心,创建订阅以接收他们有兴趣接收的消息,然后Dispose()在完成时创建订阅。

调用container.Verify()创建每种对象中的一个,导致大量这些其他瞬态实例徘徊,因为事件中心仍然知道它们的订阅。

目前,我通过Verify()在应用程序启动之前立即手动终止所有订阅来解决这个问题。然而,这感觉像是一个必须已经解决的问题,尽管我无法在文档、Stack Overflow 上或通过搜索找到答案。

也许使用有范围的生活方式是解决方案?它们似乎不相关,因为我正在构建 WPF 应用程序,但如果我知道答案,我就不会在这里问了!

1 月 12 日更新- 根据@steven 的要求,这里有一些代码来演示我的问题。

我尝试(但失败)用既可编译又足够短的内容来内联共享的东西来演示这个问题;相反,我展示了一些实际项目的代码摘录。如果您想查看整个内容,可以在 GitHub 上找到 WordTutor 项目

在我的应用程序的核心,我有一个单例IReduxStore<T>,它既封装了应用程序状态,又充当了一种事件中心。其他类订阅存储以便在应用程序状态更改时主动通知。

这是IReduxStore<T>,精简到必需品:

// IReduxStore.cs
public interface IReduxStore<T>
{
    T State { get; }
    void Dispatch(IReduxMessage message);

    IDisposable SubscribeToReference<V>(
        Func<T, V?> referenceReader,
        Action<V?> whenChanged)
        where V : class, IEquatable<V>?;
}
Run Code Online (Sandbox Code Playgroud)

订阅落实IDisposable作为确定性清理方便和习惯的方法时,不再需要预订。

商店注册为单例,绑定到特定类型的状态:

// Program.cs
container.RegisterSingleton<
    IReduxStore<WordTutorApplication>,
    ReduxStore<WordTutorApplication>>();
Run Code Online (Sandbox Code Playgroud)

实施IReduxStore<T>存储所有订阅:

private readonly HashSet<ReduxSubscription<T>> _subscriptions
    = new HashSet<ReduxSubscription<T>>();
Run Code Online (Sandbox Code Playgroud)

它们从HashSet处置时移除。

我的许多 ViewModel 接受IReduxStore<WordTutorApplication>它们的构造函数,以便它们可以订阅更新:

// VocabularyBrowserViewModel.cs
public sealed class VocabularyBrowserViewModel : ViewModelBase
{
    private readonly IReduxStore<WordTutorApplication> _store;
    private readonly IDisposable _screenSubscription;
    private readonly IDisposable _vocabularySubscription;

    public VocabularyBrowserViewModel(IReduxStore<WordTutorApplication> store)
    {
        _store = store ?? throw new ArgumentNullException(nameof(store));

        // ... elided ...

        _screenSubscription = _store.SubscribeToReference(
            app => app.CurrentScreen as VocabularyBrowserScreen,
            RefreshFromScreen);

        _vocabularySubscription = _store.SubscribeToReference(
            app => app.VocabularySet,
            RefreshFromVocabularySet);

        // ... elided ...
    }

   // ... elided ...
}
Run Code Online (Sandbox Code Playgroud)

ViewModels 注册为瞬态,因为每个窗口都需要一个唯一的实例:

// Program.cs
var desktopAssembly = typeof(WordTutorWindow).Assembly;
container.Collection.Register<ViewModelBase>(desktopAssembly);
Run Code Online (Sandbox Code Playgroud)

当不再需要它们时,ViewModel 会主动释放它们的订阅:

// VocabularyBrowserViewModel.cs
private void RefreshFromScreen(VocabularyBrowserScreen? screen)
{
    if (screen == null)
    {
        _screenSubscription.Dispose();
        _vocabularySubscription.Dispose();
        return;
    }

    Selection = screen.Selection;
    Modified = screen.Modified;
}
Run Code Online (Sandbox Code Playgroud)

Verify()在 SimpleInjector 容器上调用时,会创建每个对象的示例,包括单例IReduxStore<T>。瞬态的ViewModels(如VocabularyBrowserViewModel上面所示)也被创建,但这些情况下保持,因为他们的订阅仍然由店举办。

我尝试IDisposable在 ViewModels 上实现,但因为他们的生活方式是瞬态的,唯一的影响是在Verify()被调用时生成一个额外的警告。

更新 II,1 月 12 日

我目前的解决方法是在容器成功初始化后,作为应用程序启动的一部分手动清除所有订阅:

var store = (ReduxStore<WordTutorApplication>)
    container.GetInstance<IReduxStore<WordTutorApplication>>();
store.ClearSubscriptions();
Run Code Online (Sandbox Code Playgroud)

这感觉就像一个讨厌的黑客。首先它需要显式转换为实现类型,然后它调用一个根本不需要存在的方法。