Search code examples
c#genericsdependency-injectioninversion-of-controlcastle-windsor

Generic registration in Windsor with UsingFactoryMethod


We currently have code that looks something like below, with a factory being injected in to a lot of classes, which then call the factory to get an instance of what they want.

public class Service
{
     public Service(IFactory factory)
     {
          _car = factory.GetCar<Entity>();
     }
}

public class Car : ICar
{
}

public interface IFactory
{
    ICar<TEntity> GetCar<TEntity>();

    IBoat<TEntity> GetBoat<TEntity>();
}

public class Factory : IFactory
{
    ConnectionDetails _connectionDetails;    

    public Factory(ConnectionDetails connectionDetails)
    {
        _connectionDetails = connectionDetails;
    }

    TEntity GetCar<TEntity>()
    {
        var car = new Car<TEntity>(_connectionDetails);
        car.Initialize();
        return car;
    }
}

I was hoping to be able to create a solution that would allow for request a dependency directly on the Car<TEntity> without needing to go through the factory first.

Below is an example of installing for a single TEntity, but how would I set this up to be generic?

I've tried using open generics, but I can't see how I can get the correct return type out of .UsingFactoryMethod().

I know I can get the RequestedType out of the CreationContext, but I don't see how I can use that to solve this problem.

public class Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
                    Component.For<ICar<TEntity>>().UsingFactoryMethod(
                        kernel =>
                        {
                            var factory = kernel.Resolve<IFactory>();
                            return factory.GetCar<TEntity>();
                        }));
    }
}

Solution

  • Personally I find that factories mixed with dependency injection can be a bit of anti-pattern, since it hides implementation/creation details somewhere other than the object graph root. In addition, when mixing the two it becomes unclear who ultimately has the responsibility for creating and maintaining objects and their lifecycles.

    I'd recommend you move to allowing the container to fully handle creation details based on common base interfaces/classes.

    void Main()
    {
        var container = new WindsorContainer();
        container.Install(new Installer());
    
        var car = container.Resolve<Car>();
        car.Dump();
    }
    
    public class Service
    {
        private ICar<CarEntity> _car;
    
        public Service(ICar<CarEntity> car)
        {
            _car = car;
        }
    }
    
    public class TEntity { }
    public class CarEntity : TEntity { }
    public class BoatEntity : TEntity { }
    
    public interface ICreatable { }
    public interface ICar<TEntity> : ICreatable { }
    
    public class Car : ICar<TEntity> 
    {
        private ConnectionDetails _connectionDetails;
    
        public Car(ConnectionDetails connectionDetails)
        {
            _connectionDetails = connectionDetails; 
            Initialize();
        }
    
        public void Initialize() {}
    }
    
    public class ConnectionDetails { }
    
    public class Installer : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<ConnectionDetails>()
                         .ImplementedBy<ConnectionDetails>());
    
            container.Register(
                Classes.FromAssemblyInThisApplication()
                    .BasedOn(typeof(ICreatable))
                    .WithServiceAllInterfaces()
                    .WithServiceSelf()
                    .LifestyleTransient());
        }
    }