Considering the below implementation in castle windsor 3.4.0:
public class ExampleInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<FailoverDatabaseConnectionExecutor>()
.ImplementedBy<FailoverDatabaseConnectionExecutor>()
.LifestyleTransient());
container.Register(Component.For<DatabaseConnectionExecutor>()
.ImplementedBy<DatabaseConnectionExecutor>()
.LifestyleTransient());
container.Register(Component.For<IDatabaseConnectionExecutor>()
UsingFactoryMethod(CreateDatabaseConnectionExecutor)
.LifestyleTransient()
.IsDefault());
}
private static IDatabaseConnectionExecutor CreateDatabaseConnectionExecutor(IKernel kernel)
{
var configurationRepository = kernel.Resolve<IConfigurationRepository>();
return configurationRepository.GetSetting(ConfigurationSettings.DatabaseFailoverEnabled, () => false)
? (IDatabaseConnectionExecutor)kernel.Resolve<FailoverDatabaseConnectionExecutor>()
: kernel.Resolve<DatabaseConnectionExecutor>();
}
}
The framework is returning the following exception:
Instance FailoverDatabaseConnectionExecutor of component Late bound IDatabaseConnectionExecutor is already being tracked. The factory method providing instances of the component is reusing instances, but the lifestyle of the component is Transient which requires new instance each time. In most cases it is advised for the factory method not to be handling reuse of the instances, but to chose a lifestyle that does that appropriately. Alternatively, if you do not wish for Windsor to track the objects coming from the factory change your regustration to '.UsingFactoryMethod(yourFactory, managedExternally: true)'
This results in the dependency chain failing to resolve and a null value on the property injection on our controller.
What we are trying to achieve is a switch on the resolution based on the configuration value ConfigurationSettings.DatabaseFailoverEnabled
. We want this to happen in a transient manner on both the factory and the underlying resolved types.
From the error it would seem this is impossible, our question is how do we realise a factory style implementation whilst still maintaining a transient lifecycle on both the FailoverDatabaseConnectionExecutor
and DatabaseConnectionExecutor
EDIT:
After spending some time investigating this further it appears to be an issue related one of the objects in my dependency chain. When one of the objects implements IDisposable then this error occurs when used in conjunction with UsingFactoryMethod.
Consider the below (simplified) example:
public class ServiceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ISomeConnectionService>()
.ImplementedBy<SomeConnectionService>()
.LifestyleTransient()
.IsDefault());
container.Register(
Component.For<FailoverDatabaseConnectionExecutor>()
.ImplementedBy<FailoverDatabaseConnectionExecutor>()
.LifestyleTransient());
container.Register(Component.For<IDatabaseConnectionExecutor>()
.UsingFactoryMethod(CreateDatabaseConnectionExecutor)
.LifestyleTransient()
.IsDefault());
}
private static IDatabaseConnectionExecutor CreateDatabaseConnectionExecutor(IKernel kernel)
{
return kernel.Resolve<FailoverDatabaseConnectionExecutor>();
}
}
public interface IDatabaseConnectionExecutor
{
}
public class SomeConnectionService : ISomeConnectionService, IDisposable
{
public SomeConnectionService()
{
}
public void Dispose()
{
}
}
public interface ISomeConnectionService
{
}
public class FailoverDatabaseConnectionExecutor : IDatabaseConnectionExecutor
{
private readonly ISomeConnectionService _someConnectionService;
public FailoverDatabaseConnectionExecutor(ISomeConnectionService someConnectionService)
{
_someConnectionService = someConnectionService;
}
}
Removing IDisposable from SomeConnectionService will inject the dependency chain correctly.
Does anyone know why this is the case in castle windsor?
After working through some of the castle windsor code it seems apparent that there is an issue somewhere in regards to tracking objects that are marked for decommission. More details can be found here
We overcame this by switching to use the IHandlerSelector. This allowed us to dynamically select a dependency at run time based on configuration settings.
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<IDatabaseConnectionExecutor>()
.ImplementedBy<FailoverDatabaseConnectionExecutor>()
.LifestyleTransient());
container.Register(Component.For<IDatabaseConnectionExecutor>()
.ImplementedBy<DatabaseConnectionExecutor>()
.LifestyleTransient());
var configuration = container.Resolve<IConfigurationRepository>();
container.Kernel.AddHandlerSelector(new DatabaseExecutorHandlerSelector(configuration));
container.Release(configuration);
}
}
public class DatabaseExecutorHandlerSelector : IHandlerSelector
{
private readonly IConfigurationRepository configurationRepository;
public DatabaseExecutorHandlerSelector(IConfigurationRepository configurationRepository)
{
this.configurationRepository = configurationRepository;
}
public bool HasOpinionAbout(string key, Type service)
{
return service == typeof(IDatabaseConnectionExecutor);
}
public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
var failoverEnabled = configurationRepository.GetSetting(ConfigurationSettings.BagDatabaseFailoverEnabled, () => false);
var implementationToUse = failoverEnabled ?
typeof(FailoverDatabaseConnectionExecutor) :
typeof(DatabaseConnectionExecutor);
return handlers.First(x => x.ComponentModel.Implementation == implementationToUse);
}
}