Search code examples
c#dependency-injectionscopecastle-windsordispose

Using Castle Windsor I need a new instance of an object when starting a new scope


I am trying to achieve object construction as shown below which creates a new DbContext within the using and hence gets disposed when leaving the using scope. So as shown below I have created two processor objects in two separate using scopes. I want to achieve this using Castle Windsor.

        using (var context = new DbContext())
        {
            var processor = new Processor(context, new Parser(context, new Logger(context)), new Logger(context));
        }

        using (var context = new DbContext())
        {
            var processor = new Processor(context, new Parser(context, new Logger(context)), new Logger(context));
        }

I have asked a similar question before Castle Windsor propogating inline dependencies which suggested using scopes but I can't get it to work the way I want. See the entire program below.

using Castle.MicroKernel.Lifestyle;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using System;

namespace IOCTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            var container = new WindsorContainer();

            container
                .Register(Component.For<IProcessor>()
                .ImplementedBy<Processor>());

            container
                .Register(Component.For<IParser>()
                .ImplementedBy<Parser>());

            container
                .Register(Component.For<ILogger>()
                .ImplementedBy<Logger>());

            container.Register(Component.For<DbContext>()
                .ImplementedBy<DbContext>()
                .LifeStyle
                .Scoped());

            using (container.BeginScope())
            {
                var processor = container.Resolve<IProcessor>();
            }

            using (container.BeginScope())
            {
                var processor = container.Resolve<IProcessor>();
            }
        }
    }

    public class DbContext : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("DbContext disposed.");
        }
    }

    public class Processor : IProcessor
    {
        private readonly DbContext _context;
        private readonly ILogger _logger;
        private readonly IParser _parser;

        public Processor(DbContext context, IParser parser, ILogger logger)
        {
            _context = context;
            _parser = parser;
            _logger = logger;
        }
    }

    public class Parser : IParser
    {
        private readonly DbContext _context;
        private readonly ILogger _logger;

        public Parser(DbContext context, ILogger logger)
        {
            _context = context;
            _logger = logger;
        }
    }

    public class Logger : ILogger
    {
        private readonly DbContext _context;

        public Logger(DbContext context)
        {
            _context = context;
        }
    }

    public interface IProcessor
    {
    }

    public interface IParser
    {
    }

    public interface ILogger
    {
    }
}

On exiting the first container.BeginScope() the Dispose method on DbContext is called, but on the second call to container. BeginScope() the Dispose method is not called when exiting the second scope.

This implies the container is given me the same disposed DbContext instance when the second container. BeginScope() is called. In essence, I need the DbContent to be transient within the container.BeginScope() and the container give me a new DbContext instance on every newly created scope so it is always disposed when the scope is exited.

Hopefully that makes sense.


Solution

  • The IProcessor, IParser, and ILogger also need to be registered as Scoped.

    container.Register(Component.For<IProcessor>()
        .ImplementedBy<Processor>()
        .LifeStyle.Scoped());
    

    Do this for all components that depend on DBContext.

    If no lifestyle is explicitly set the default is singleton. So the first time you resolve an IProcessor the singleton is instantiated, it's dependencies are resolved including the scoped DBContext. At the end of the first scope the DBContext is released and disposed, but the singleton IProcessor still has a referenced to that disposed DBContext. When you resolve an IProcessor in the second scope the already instantiated singleton is returned, containing the already disposed DBContext.