Jason
Jason

Reputation: 174

What's the best way to call dispose on Unity Containers?

I have a a completed (and broken) C# app below that generates a stack overflow exception. If you review the source code, you'll see why there's a stack overflow exception, so I'm not really looking at diagnosing WHY it happens, I want to know what the best way of handling it is.

1) All references to unity are encapsulated inside a Class named Registry so I can upgrade without difficulty. I don't want unitycontainer littering other classes where possible. In theory, I should be able to upgrade to 5 if/when it comes out, or even swap it out with ninject or other DI framework if I were to have a drastic change of disposition.

2) I want the Registry to be controlled by the unity container so that it can be used in the constructors for the container controlled classes. (eg FirstSingleInstance)

3) Both IRegistry and Registry inherit from IDisposable because I assume it's good practice to dispose the unity containers.

4) Registry constructs the Unity Container in it's own constructor, so I assume I should also dispose the unity container when registry.dispose is called.

5) all other classes that are controlled by Registry are expected to be single instance classes, so I register them with a ContainerControlledLifetimeManager. I expect those instances will be disposed when the container gets disposed.

What is the best practice for dealing with this situation?

a) do not call dispose on Registry -- let it live for the life of the process thread?

b) Don't try to have Registry (and by extension, UnityContainer) controlled by the unity container. That way calling dispose on Registry won't cause a stackoverflow exception. How I would then have unity construct the FirstSingleInstance class is something I'd have to review.

d) other?

Here's the app that I wrote that has all the relevant pieces

using System;
using Microsoft.Practices.Unity;

namespace DIProblem.Console
{
    class Program
    {
        static void Main(string[] args)
        {
            IRegistry registry = CreateRegistry();
            IFirstSingleInstance theInstance = registry.Resolve<IFirstSingleInstance>();
            theInstance.DoThis();
            registry.Dispose();   // stack overflow here because of infinite dispose loop
        }

        static IRegistry CreateRegistry() => new Registry();
    }

    public class FirstSingleInstance : IFirstSingleInstance
    {
        private IRegistry _registry;

        public FirstSingleInstance(IRegistry reg)
        {
            _registry = reg;
        }

        public void DoThis()
        {

            System.Console.WriteLine("This Was Done.");

            _registry.Resolve<ISecondSingleInstance>().DoThisToo();

        }
    }

    public class SecondSingleInstance : ISecondSingleInstance
    {
        private IRegistry _registry;

        public SecondSingleInstance(IRegistry reg)
        {
            _registry = reg;
        }

        public void DoThisToo()
        {
            System.Console.WriteLine("This Was Done too.");
        }
    }

    public interface ISecondSingleInstance
    {
        void DoThisToo();
    }


    public interface IFirstSingleInstance
    {
        void DoThis();
    }


    public class Registry : IRegistry, IDisposable
    {
        public Registry()
        {
            _container = new UnityContainer();

            RegisterInstance<IFirstSingleInstance, FirstSingleInstance>();
            RegisterInstance<ISecondSingleInstance, SecondSingleInstance>();

            _container.RegisterInstance<IRegistry>(this);
        }

        private UnityContainer _container;

        public void RegisterInstance<T1, T2>() where T2 : class, T1 => _container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());

        public T Resolve<T>() => _container.Resolve<T>();

        public void Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            _container?.Dispose();
            _container = null;
        }
    }

    public interface IRegistry : IDisposable
    {
        T Resolve<T>();
        void RegisterInstance<T1, T2>() where T2 : class, T1;
    }
}

Thank you for helping out in whatever way seems reasonable.

Upvotes: 0

Views: 4750

Answers (1)

Steven
Steven

Reputation: 172646

The following code refrains from using the Service Locator anti-pattern and instead relies solely on Constructor Injection as pattern for applying Inversion of Control. The result is a simpler, more maintainable and more testable application that doesn't cause any stackoverflow exceptions.

class Program
{
    static void Main(string[] args)
    {
        using (var container = Registry.BuildContainer())
        {
            var theInstance = registry.Resolve<IFirstSingleInstance>();
            theInstance.DoThis();
        }
    }
}

public static class Registry
{
    public static UnityContainer BuildContainer()
    {
        var container = new UnityContainer();

        container.RegisterType<IFirstSingleInstance, FirstSingleInstance>(Singleton);
        container.RegisterType<ISecondSingleInstance, SecondSingleInstance>(Singleton);

        return container;
    }

    private static ContainerControlledLifetimeManager Singleton => 
        new ContainerControlledLifetimeManager();
}

public interface ISecondSingleInstance
{
    void DoThisToo();
}

public interface IFirstSingleInstance
{
    void DoThis();
}

public class FirstSingleInstance : IFirstSingleInstance
{
    private ISecondSingleInstance _second;

    public FirstSingleInstance(ISecondSingleInstance second)
    {
        _second = second;
    }

    public void DoThis()
    {
        System.Console.WriteLine("This Was Done.");
        _second.DoThisToo();
    }
}

public class SecondSingleInstance : ISecondSingleInstance
{
    public SecondSingleInstance(/* other dependencies here */)
    {
    }

    public void DoThisToo()
    {
        System.Console.WriteLine("This Was Done too.");
    }
}

Upvotes: 2

Related Questions