Ninject使用短暂作用域和Dispose

7
我有一个控制台应用程序,使用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语句包装所有用法即可。

2个回答

10

Ninject2中,您可以通过以下方式实现:

Bind<IService>().To<ServiceImpl>().InScope(ctx => ...);
例如,InRequestScope() 使用的回调函数是:
ctx => HttpContext.Current

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

您可以在控制台中拥有一个静态变量来引用控制生命周期的对象。

public static object LifetimeController = new object();

您可以将此注册为您的生命周期控件对象。

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

每次当您想要刷新对象时,您可以拥有一个类似于这样的方法

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

更多信息请参见此处此处


1
我点赞是因为这很棒……但我似乎无法使用测试使其正常工作。您不需要让SomeClass实现IDisposable吗?然后在调用StartNewLifetime()时,Ninject应该调用先前的SomeClass实例上的Dispose()方法?无论如何,这不是我的问题,所以我会退到一边,让OP做出决定… - McGarnagle
@McGarnagle 这是详细信息:回调返回的对象还可以实现Ninject中的INotifyWhenDisposed接口,如果您想确定性地停用“拥有”的实例。如果作用域对象实现了此接口,则在Dispose()时,它拥有的任何实例都将立即停用。 - qujck
3
我将此标记为答案。我最终所做的是Bind<IService>().To<ServiceImpl>().InScope(ctx => new object()),这使得每次都会得到一个新的对象,同时仍会回收ServiceImpl。尽管我需要等待垃圾回收,但当我调用GC.Collect()时,它确实释放了相关的可处理对象。 - Michael

-1

使用 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);
        }
    }
}

这是我考虑的另一个选项。我知道最终器会被调用。谢谢你。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接