c# - 验证过程中如何清理 SimpleInjector 创建的实例?

标签 c# wpf .net-core simple-injector

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

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

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

调用 container.Verify()创建每种对象中的一种,导致许多这些原本短暂的实例徘徊,因为事件中心仍然知道它们的订阅。

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

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

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

我尝试(但失败了)用一些既可编译又短到可以内联共享的东西来证明这个问题;相反,我展示了实际项目的一些代码摘录。如果您想查看全部内容,WordTutor project is on GitHub .

在我的应用程序的核心,我有一个单例 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>?;
}

订阅实现 IDisposable当不再需要订阅时,作为确定性清理的一种方便且惯用的方式。

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

// Program.cs
container.RegisterSingleton<
    IReduxStore<WordTutorApplication>,
    ReduxStore<WordTutorApplication>>();
IReduxStore<T>的执行存储所有订阅:

private readonly HashSet<ReduxSubscription<T>> _subscriptions
    = new HashSet<ReduxSubscription<T>>();

它们已从 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 ...
}

ViewModel 被注册为 transient ,因为每个窗口都需要一个唯一的实例:

// Program.cs
var desktopAssembly = typeof(WordTutorWindow).Assembly;
container.Collection.Register<ViewModelBase>(desktopAssembly);

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

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

    Selection = screen.Selection;
    Modified = screen.Modified;
}

Verify()在 SimpleInjector 容器上调用,创建每个对象的示例,包括单例 IReduxStore<T> . transient View 模型(例如上面显示的 VocabularyBrowserViewModel)也被创建,但这些实例仍然是 直播因为他们的订阅仍然由商店持有。

我尝试实现 IDisposable在 ViewModel 上,但由于它们的生活方式是短暂的,唯一的效果是在 Verify() 时生成附加警告。被称为。

更新 II,1 月 12 日 :

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

var store = (ReduxStore<WordTutorApplication>)
    container.GetInstance<IReduxStore<WordTutorApplication>>();
store.ClearSubscriptions();

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

最佳答案

尝试设置EnableAutoVerification在 Simple Injector 5.0 (https://simpleinjector.org/ReferenceLibrary/html/P_SimpleInjector_ContainerOptions_EnableAutoVerification.htm) 中为 false

关于c# - 验证过程中如何清理 SimpleInjector 创建的实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59656914/

相关文章:

wpf - 如何在MVVM-WPF中的某个事件上激活新页面?

.net - System.Reactive 5.0 中缺少 DispatcherScheduler

c# - WPF 控件中的 native Win32 窗口

asp.net-core - 带有 IIS 的 DotNetCore 2.2 应用程序无法工作 - HTTP 错误 500.0 - ANCM 进程内处理程序加载失败

c# - 如何捕获带缩进的字符串组?

c# - C# 中的正则表达式

docker - Bitbucket管道。无法指定WindowsServerCore Docker镜像

c# - 使用依赖注入(inject)通过构造函数反射调用c#类方法

C# 从 IGrouping 中选择不同的值

c# - HTML Agility Pack - 从 SPAN 标签内的第一个 STRONG 标签获取文本