Ninject Transient Scope with Dispose
Asked Answered
H

2

7

I have a console app that uses kernel.Get<SomeClass>(); However, SomeClass has a dependency on SomeDisposableClass. How can I set up my binding to dispose of SomeDisposableClass when SomeClass is garbage collected? My MVC app uses InRequestScope and that works great, but there doesn't seem to be an analogous scope for console apps.

Example here:

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

    private SomeDisposableClass C { get; set; }

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

My module

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

My console app

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

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

I'd like for SomeDisposableClass to be disposed when SomeFunc is finished (or when the garbage collector is called). But I'm not sure of which binding scope to use. InTransientScope doesn't ever call dispose. Do I just have to make SomeClass disposable and implement Dispose() and wrap all my usages in the console app with a using statement?

Hydropic answered 12/12, 2013 at 16:26 Comment(0)
S
11

In Ninject2, you can do this by:

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

For example, the callback used for InRequestScope() is:

ctx => HttpContext.Current

Since HttpContext.Current is set to a new instance of HttpContext on each web request, only a single instance of the service will be activated for each request, and when the request ends and the HttpContext is (eventually) collected, the instances will be deactivated.

You can have a static variable within your console to reference an object that will control lifetime.

public static object LifetimeController = new object();

You can register this as your lifetime control object

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

And each time you want to refresh the objects you can have a method like this

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

See here and here for more information

Sproul answered 12/12, 2013 at 17:11 Comment(3)
I upvoted because this is awesome ... but I can't seem to get it working with a test. You would need to have SomeClass implement IDisposable, would you not? And Ninject should then call Dispose() on the previous SomeClass instance when you invoke StartNewLifetime()? Anyway, not my question, so I will step aside so the OP may render a verdict ...Nalchik
@Nalchik Here's the detail: The object returned by the callback can also implement INotifyWhenDisposed, an interface from Ninject, if you want to deterministically deactivate the "owned" instances. If the scoping object implements this interface, when it is Dispose()'d, any instances it owns will be deactivated immediately.Sproul
I marked this as the answer. What I ended up doing was Bind<IService>().To<ServiceImpl>().InScope(ctx => new object()); this gives me a new object every time and still GCs the ServiceImpl. I did have to wait for garbage collection, but when I did GC.Collect() it did indeed free the dependent disposable object.Hydropic
N
-1

Use InTransientScope -- then the Ninject container will not hold any reference to the object. That way SomeClass will be GC'd when it goes out of scope at the end of SomeFunc. All you need to do is have its finalizer dispose of the SomeDisposableClass instance:

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

Here's how I was testing:

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);
        }
    }
}
Nalchik answered 12/12, 2013 at 17:13 Comment(1)
This was another option I was thinking of. I knew that the finalizer would be called. Thanks for this.Hydropic

© 2022 - 2024 — McMap. All rights reserved.