grumpydev / TinyIoC

An easy to use, hassle free, Inversion of Control Container for small projects, libraries and beginners alike.
MIT License
830 stars 235 forks source link

Registration required on child container for proper disposal #62

Open tonysneed opened 10 years ago

tonysneed commented 10 years ago

If services are registered on a parent container, calling Dispose on a child container will not call dispose on registered services. Services must be registered directly on each child container. This is not easy to do with a Web API dependency resolver, where you would create a child container on BeginScope. The only workaround I have found is to call AutoRegister on the child container, but this is not ideal.

It seems like calling Dispose on a child container should Dispose dependencies that were registered on the parent container if they were not also registered on the child container?

kentcb commented 10 years ago

You describe the opposite behavior to what I would expect (i.e. the current behavior). The parent container would typically outlive the child, so disposing the parent when you dispose the child would mean the parent can't have any more children. Writing that makes me want to go and hug my kids...

The real question here is why you feel you need to register the services in the child container? If the services in question should be available to all children, why not just register them in the parent and let the bubbling behavior of resolutions find those services when you resolve against the child container?

jonie commented 10 years ago

Ok, @kentcb is this expected behavior (see code)?

using System;
using TinyIoC;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var parent = TinyIoCContainer.Current;
            parent.Register<IFoo, Disposable>().AsMultiInstance();

            var child = parent.GetChildContainer();
            var instance = child.Resolve<IFoo>();

            instance.Some();
            child.Dispose();

            Console.WriteLine("Child container was disposed, but resolved via child container instance not?");
            Console.ReadLine();
        }
    }

    interface IFoo {
        void Some();
    }

    class Disposable : IDisposable, IFoo
    {
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool b)
        {
            Console.WriteLine("Disposed!");
        }

        ~Disposable()
        {
            Dispose(false);
        }

        public void Some()
        {
            Console.WriteLine("...work...");
        }
    }
}
kentcb commented 10 years ago

@jonie: to be clear, I can't answer for the author of TinyIoC - these are just my thoughts.

Your example still seems to have the correct behavior to me. By registering IFoo on the parent container, you're tying the lifetime of any IFoos to the parent container. If you want the IFoo's lifetime to be tied to the child container, why wouldn't you register it there?

tonysneed commented 10 years ago

The real question I have is how to write a dependency resolver for Web API using TinyIoC, in which the scope is disposed at the conclusion of each request. Does TinyIoC have the notion of scope?

MihaMarkic commented 5 years ago

Has this ever been solved? @tonysneed raised a valid point and @jonie provided a valid example of how it should be. If that's not what it should be, then either we misunderstood the purpose if child container or there is another way to deal with limited lifetimes.

niemyjski commented 5 years ago

I don't think so, but I haven't dived in... Would you mind taking a look? I'd agree with @kentcb's last comment.

MihaMarkic commented 5 years ago

@niemyjski Here is an example. I would register all stuff globally, at application start. And at some point I'd like to have a limited lifespan. Doing it your way, I'd have to create child container and register everything again (or at least what I need) even though it is already registered. Why not create a child container, use it and then dispose it to clear whatever was created? What purpose has child container otherwise? To override few registrations?

niemyjski commented 5 years ago

That makes sense and is straight forward. My concern though, is if you have a parent and child containers and you have a parent object like IFoo and you dispose of the child container. Then dispose should be called on all objects in the child container. So now you have a disposed object registered in the parent which might be a singleton.

MihaMarkic commented 5 years ago

@niemyjski Singletons shouldn't be disposed by child container IMO.

niemyjski commented 5 years ago

If you dispose the child container than dispose should be called on the singletons registered in the child container..

MihaMarkic commented 5 years ago

@niemyjski You mean "singletons that are resolved and created (because they don't exist on parent container) on child container"? That's an option as well. Either that or transfer ownership to parent-most container. I would assume the later, but both should work.

niemyjski commented 5 years ago

Seems weird that parent and child containers could have different scopes but share the same instance. It's almost like if anything is registered in the child container we dispose of it, otherwise get it from the parent container instance. I'm just trying to think of what's straight forward, but I'm all in for what the general consensus is.