【问题标题】:Ninject Transient Scope with Dispose使用 Dispose 注入瞬态作用域
【发布时间】:2013-12-13 01:33:46
【问题描述】:

我有一个使用 kernel.Get<SomeClass>(); 的控制台应用程序,但是,SomeClass 依赖于 SomeDisposableClass。当SomeClass 被垃圾回收时,如何设置我的绑定以处理SomeDisposableClass?我的 MVC 应用程序使用 InRequestScope,效果很好,但控制台应用程序似乎没有类似的范围。

这里的例子:

public class SomeClass {
    public SomeClass(SomeDisposableClass c) {
        this.C = c;
    }

    private SomeDisposableClass C { get; set; }

    // ... Business Methods ... //
}

我的模块

kernel.Bind<ISomeClass>().To<SomeClass>().In???Scope()

我的控制台应用

public static void Main() {
    SomeFunc();
    SomeFunc();
    Console.ReadLine();
}

public static void SomeFunc() {
    ISomeClass someClass = kernel.Get<ISomeClass>();
    // work
}

我希望在 SomeFunc 完成时(或在调用垃圾收集器时)处理 SomeDisposableClass。但我不确定要使用哪个绑定范围。 InTransientScope 从不调用 dispose。我是否只需将SomeClass 设为一次性并实现Dispose() 并使用using 语句将我的所有用法包装在控制台应用程序中?

【问题讨论】:

    标签: c# dependency-injection scope ninject


    【解决方案1】:

    在 Ninject2 中,您可以这样做:

    Bind<IService>().To<ServiceImpl>().InScope(ctx => ...);
    

    例如InRequestScope()使用的回调是:

    ctx => HttpContext.Current
    

    由于 HttpContext.Current 在每个 Web 请求上都设置为 HttpContext 的新实例,因此每个请求只会激活一个服务实例,并且当请求结束并且 HttpContext 是(最终)收集,实例将被停用。

    您可以在控制台中使用静态变量来引用将控制生命周期的对象。

    public static object LifetimeController = new object();
    

    您可以将其注册为您的生命周期控制对象

    Bind<IService>().To<ServiceImpl>().InScope(ctx => LifetimeController);
    

    而且每次你​​想刷新对象时都可以有这样的方法

    public static void StartNewLifetime()
    {
        LifetimeController = new object();
    }
    

    请参阅herehere 了解更多信息

    【讨论】:

    • 我投了赞成票,因为这太棒了……但我似乎无法通过测试让它工作。你需要让SomeClass 实现IDisposable,不是吗?然后当您调用StartNewLifetime() 时,Ninject 应该在先前的SomeClass 实例上调用Dispose()?无论如何,这不是我的问题,所以我会靠边站,以便 OP 做出裁决......
    • @McGarnagle 以下是详细信息:如果您想确定性地停用“拥有”实例,回调返回的对象还可以实现INotifyWhenDisposed,这是来自Ninject 的接口。如果作用域对象实现了这个接口,当它被 Dispose()'d 时,它拥有的任何实例都将立即停用。
    • 我将此标记为答案。我最终做的是Bind&lt;IService&gt;().To&lt;ServiceImpl&gt;().InScope(ctx =&gt; new object());,这每次都会给我一个新对象,并且仍然 GCs ServiceImpl。我确实必须等待垃圾收集,但是当我执行 GC.Collect() 时,它确实释放了依赖的一次性对象。
    【解决方案2】:

    使用InTransientScope——那么Ninject 容器将不会保存对该对象的任何引用。这样SomeClass 将在SomeFunc 末尾超出范围时被GC 处理。您需要做的就是让其终结器处理 SomeDisposableClass 实例:

    public class SomeClass : IDisposable {
        ~SomeClass() {
            if (this.C != null) this.C.Dispose();
        }
    }
    

    这是我的测试方式:

    class Program
    {
        private static IKernel _kernel;
    
        static void Main(string[] args)
        {
            _kernel = new StandardKernel();
            _kernel.Bind<ISomeClass>().To<SomeClass>().InTransientScope();
    
            while (true)
            {
                LifetimeController = new object();
                SomeFunc();
                Thread.Sleep(10);
            }
        }
    
        public static void SomeFunc()
        {
            _kernel.Get<ISomeClass>();
        }
    
        public interface ISomeClass { }
        public class SomeClass : ISomeClass
        {
            public SomeDisposableClass C = new SomeDisposableClass();
            ~SomeClass()
            {
                Console.WriteLine("{0} finalized", this);
                C.Dispose();
            }
        }
        public class SomeDisposableClass : IDisposable
        {
            private byte[] bytes = new byte[1000000];
            public void Dispose()
            {
                Console.WriteLine("{0} disposed", this);
            }
        }
    }
    

    【讨论】:

    • 这是我想到的另一个选择。我知道终结器会被调用。谢谢你。
    猜你喜欢
    • 2021-01-14
    • 2017-06-10
    • 2021-07-16
    • 1970-01-01
    • 2021-04-28
    • 2011-01-26
    • 2020-08-23
    • 2017-10-15
    • 1970-01-01
    相关资源
    最近更新 更多