Search code examples
structuremapstructuremap3

Why in StructureMap are Singleton classes disposed of and transients aren't


In my application I've noticed that if I mark a class in the SM registry as a Singleton type it gets disposed of, however if don't specify any Singleton it doesn't get disposed of. What and why are the reasons for this?

 public class IoC
{
    public static IContainer Init()
    {
        var container = new Container(x =>
        {
            x.Scan(s => {
                s.TheCallingAssembly();
                s.AssembliesFromApplicationBaseDirectory();
                s.WithDefaultConventions();                   
            });

            // disposed is called on this class but not if .Singleton() is removed
            x.For<IMyService>().Singleton();
        });

        return container;
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var container = IoC.Init())
        {
            var theStory1 = container.GetInstance<MyService>();
            theStory1.TheMethod();              

        }

    }
}

Solution

  • Singleton lifecycle is bound to the container's scope thus when disposing the container it takes care to dispose all singletons implementing IDisposable. With transients and other lifecycles like HttpContextScoped it is up to developer to dispose them manually when no longer need them.

    Transient disposables are tricky a little bit in terms of how it should be handled. Imagine case like this one below:

    public class ClassWithDisposableTypeDependency
    {
        private readonly ISampleDisposable disposableType;
    
        public ClassWithDisposableTypeDependency(ISampleDisposable disposableType)
        {
            this.disposableType = disposableType;
        }
    
        public void SomeAction()
        {
            using (this.disposableType)
            {
                this.disposableType.DoSomething();
            }
        }
    }
    

    What will happend when SomAction() won't be executed? Dispose won't be called on disposableType field. In fact in this case ClassWithDisposableTypeDependency should also implement IDisposable and dispose its disposable dependencies.

    But there is a better way to handle such cases. In mentioned case the main issue is to defer creation of a dependency to a moment when we really need that objects. We can achieve that in many ways: func, lazy, factory, etc. Here is possible solution with usage of func.

    public class ClassWithDisposableTypeFuncDependency
    {
        private readonly Func<ISampleDisposable> disposableTypeFactory;
    
        public ClassWithDisposableTypeFuncDependency(Func<ISampleDisposable> disposableTypeFactory)
        {
            this.disposableTypeFactory = disposableTypeFactory;
        }
    
        public void SomeAction()
        {
            var disposable = this.disposableTypeFactory();
    
            using (disposable)
            {
                disposable.DoSomething();
            }
        }
    }
    

    This is how we need to setup it in StructureMap:

    var container = new Container(c => c.For<ISampleDisposable>().Use<SampleDisposable>());
    
    var clazz = container.GetInstance<ClassWithDisposableTypeFuncDependency>();
    
    clazz.SomeAction(); // dependency is created and disposed
    

    Hope this helps!