使用 NamedScope 扩展时如何让 Ninject 停用范围定义对象?

如何解决使用 NamedScope 扩展时如何让 Ninject 停用范围定义对象?

我有一个 Windows 窗体应用程序,我使用 Ninject 和 Program.Main() 入口点作为组合根。我有一个在瞬态范围内创建的 Form1 类型的主要形式。然后该表单创建了一个 Form2 类型的第二个表单,它定义了一个名为“Form2”的命名范围。 Form2 有自己的依赖项,这些依赖项在 Form2 命名范围内绑定了 ToSelf()。

我需要实现的是在关闭 Form2 时停用 Form2 及其依赖项。如果我将 OnDeactivation() 调用添加到 Form2 及其依赖项,则在关闭 Form2 时不会调用 OnDeactivation 操作。仅当我关闭主窗体并退出主 Application.Run() 调用时才调用依赖项,而不是 Form2 本身,此时 Ninject 内核被释放。

本质上,它的行为就好像 Ninject 没有管理 Form2 的生命周期。我无法从文档中判断这是否是预期的行为。

我认为这可能是由于 Windows 在某处保留了对 Form2 的强引用,所以我尝试在表单上调用 Dispose(),并确保我没有在自己的代码中保留任何引用。尽管如此,当我关闭 Form2 时,不会调用 Form2 OnDeactivation 操作。

我也尝试在 Form2 关闭后调用 GC.Collect(),没有任何变化。

我的最终目标是使用 Ninject.Extensions.AppccelerateEventBroker 扩展在 Ninject 激活它们时自动注册 Form2 及其对事件代理的依赖项,并在 Ninject 停用它们时从事件代理中取消注册它们。当我尝试这个时,由于 Ninject 在我关闭 Form2 时没有停用 Form2 及其依赖项,事件代理仍然注册了它们,并且它们仍然接收事件,即使在表单关闭之后,这是我需要解决的主要问题。

那么,如何让 Ninject 在关闭时停用 Form2(定义范围的对象)?

一些重现代码:

using System;
using System.Windows.Forms;
using Ninject;
using Ninject.Extensions.NamedScope;

namespace FormGcTest
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            using (var container = new StandardKernel()) {
                container.Bind<Form1>()
                    .ToSelf()
                    .OnActivation(x => Console.WriteLine("Form1 activated"))
                    .OnDeactivation(x => Console.WriteLine("Form1 deactivated"))
                ;

                container.Bind<Form2>()
                    .ToSelf()
                    .OnActivation(x => Console.WriteLine("Form2 activated"))
                    .OnDeactivation(x => Console.WriteLine("Form2 deactivated"))
                    .DefinesNamedScope("Form2")
                ;

                container.Bind<Form2Dependency>()
                    .ToSelf()
                    .InNamedScope("Form2")
                    .OnActivation(x => Console.WriteLine("Form2Dependency activated"))
                    .OnDeactivation(x => Console.WriteLine("Form2Dependency deactivated"))
                ;

                var form1 = container.Get<Form1>();
                Application.Run(form1);
            }
        }
    }

    public partial class Form1 : Form
    {
        private readonly Form2 mForm2;

        public Form1(Form2 form2)
        {
            mForm2 = form2;
            mForm2.FormClosed += Form2_FormClosed;
            InitializeComponent();
        }

        private void Form2_FormClosed(object sender,FormClosedEventArgs e)
        {
            mForm2 = null;
        }

        ~Form1()
        {
            Console.WriteLine("Form1 finalizer");
        }

        private void BtnGc_Click(object sender,EventArgs e)
        {
            GC.Collect();
        }

        private void Form1_Shown(object sender,EventArgs e)
        {
            mForm2.Show();
        }
    }

    public partial class Form2 : Form
    {
        private readonly Form2Dependency mForm2Dependency;

        public Form2(Form2Dependency form2Dependency)
        {
            mForm2Dependency = form2Dependency;

            InitializeComponent();

            FormClosed += Form2_FormClosed;
        }

        private void Form2_FormClosed(object sender,FormClosedEventArgs e)
        {
            Dispose();
        }

        ~Form2()
        {
            Console.WriteLine("Form2 finalizer");
        }

        private void Form2_Shown(object sender,EventArgs e)
        {
            mForm2Dependency.DoSomething();
        }
    }

    public class Form2Dependency
    {
        ~Form2Dependency()
        {
            Console.WriteLine("Form2Dependency finalizer");
        }

        public void DoSomething()
        {
            Console.WriteLine("Doing something");
        }
    }
}

当我运行这个测试应用程序时,Form1 出现,然后 Form2 出现,正如预期的那样。此时,控制台输出显示:

Form2Dependency activated
Form2 activated
Form1 activated
Doing something

然后,如果我关闭 Form2,则没有额外的输出。此时,我期待看到 Ninject 已停用 Form2 和 Form2Dependency。

如果我随后关闭 Form1,并且应用程序退出,我将看到以下额外的控制台输出:

Form2Dependency deactivated
Form2Dependency finalizer

因此您可以看到 Ninject 正在停用 Form2Dependency,而不是 Form2 本身,它仅在应用程序退出时才这样做。

我确定我在这里遗漏了一些东西来让 Ninject 管理 Form2 的生命周期并在我关闭它时让它停用它及其依赖项。我就是想不通我错过了什么。

编辑:在我的问题的原始版本中,我意识到 Form1 在其 mForm2 成员变量中持有对 Form2 注入实例的引用。在此编辑中,我添加了代码让 Form1 订阅 Form2 的 FormClosed 事件,并将该引用设置为 null,这将使 Form2 实例符合垃圾回收条件。即使我在关闭 Form2 后强制运行 GC,Ninject 仍然不会停用 Form2 实例和 Form2Dependency 实例。

编辑 2:通过将 INotifyWhenDisposed 添加到 Form2,我取得了一些进展。现在,当 Form2 关闭时,Ninject 会停用 Form2Dependency。不过,它仍然不会停用 Form2 本身。

编辑 3:我想出了一个可以满足我需求的解决方法,但似乎仍然不是最优雅的解决方案。我正在做的是有一个名为 FormScopeProvider 的单例服务,它有一个 CreateNew() 方法,该方法创建并返回一个 FormScope 实例并将其存储在名为 Current 的公共属性中。 FormScope 是一个非常简单的类,它只实现 INotifyWhenDisposed 接口。然后我在自定义范围内绑定 Form2,使用 FormScopeProvider:

container.Bind<Form2>()
    .ToSelf()
    .InScope(x => x.Kernel.Get<FormScopeProvider>().CreateNew())
    .OnActivation(x => Console.WriteLine($"Form2 {x.GetHashCode()} activated"))
    .OnDeactivation(x => Console.WriteLine($"Form2 {x.GetHashCode()} deactivated"))
;

然后在我的主表单中,当我请求一个新的 Form2 实例(我通过注入的工厂完成)时,我从 FormScopeProvider 中获取 Current FormScope 实例,并将其存储在字典中,其中键是 Form2刚刚创建的实例,值是我刚从提供者那里得到的 FormScope 对象。我还订阅了 Form2 的 FormClosed 事件,并在处理程序中从字典中获取关联的 FormScope,并对其调用 Dispose():

private void BtnShowForm2_Click(object sender,EventArgs e)
{
    var form2 = mFormFactory.CreateForm2();
    mFormScopes.Add(form2,mFormScopeProvider.Current);
    form2.FormClosed += Form2_FormClosed;
    form2.Show();
}

private void Form2_FormClosed(object sender,FormClosedEventArgs e)
{
    var formScope = mFormScopes[sender as Form2];
    mFormScopes.Remove(sender as Form2);
    formScope.Dispose();
}

这会触发 Ninject 停用该范围内的所有内容,包括 Form2 实例及其 Form2Dependency 实例。

这适用于我的实际应用程序,但我仍然很想知道是否有一种更简洁的方法让 Ninject 在表单定义了命名范围时立即停用表单。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-