Using SimpleInjector, I am trying to register an entity that depends on values retrieved from another registered entity. For example:
Settings
- Reads settings values that indicate the type of SomeOtherService
the app needs.
SomeOtherService
- Relies on a value from Settings
to be instantiated (and therefore registered).
Some DI containers allow registering an object after resolution of another object. So you could do something like the pseudo code below:
container.Register<ISettings, Settings>();
var settings = container.Resolve<ISettings>();
System.Type theTypeWeWantToRegister = Type.GetType(settings.GetTheISomeOtherServiceType());
container.Register(ISomeOtherService, theTypeWeWantToRegister);
SimpleInjector does not allow registration after resolution. Is there some mechanism in SimpleInjector that allows the same architecture?
A simple way to get this requirement is to register all of the available types that may be required and have the configuration ensure that the container returns the correct type at run time ... it's not so easy to explain in English so let me demonstrate.
You can have multiple implementations of an interface but at runtime you want one of them, and the one you want is governed by a setting in a text file - a string
. Here are the test classes.
public interface IOneOfMany { }
public class OneOfMany1 : IOneOfMany { }
public class OneOfMany2 : IOneOfMany { }
public class GoodSettings : ISettings
{
public string IWantThisOnePlease
{
get { return "OneOfMany2"; }
}
}
So let's go ahead and register them all:
private Container ContainerFactory()
{
var container = new Container();
container.Register<ISettings, GoodSettings>();
container.RegisterAll<IOneOfMany>(this.GetAllOfThem(container));
container.Register<IOneOfMany>(() => this.GetTheOneIWant(container));
return container;
}
private IEnumerable<Type> GetAllOfThem(Container container)
{
var types = OpenGenericBatchRegistrationExtensions
.GetTypesToRegister(
container,
typeof(IOneOfMany),
AccessibilityOption.AllTypes,
typeof(IOneOfMany).Assembly);
return types;
}
The magic happens in the call to GetTheOneIWant
- this is a delegate
and will not get called until after the Container
configuration has completed - here's the logic for the delegate
:
private IOneOfMany GetTheOneIWant(Container container)
{
var settings = container.GetInstance<ISettings>();
var result = container
.GetAllInstances<IOneOfMany>()
.SingleOrDefault(i => i.GetType().Name == settings.IWantThisOnePlease);
return result;
}
A simple test will confirm it works as expected:
[Test]
public void Container_RegisterAll_ReturnsTheOneSpecifiedByTheSettings()
{
var container = this.ContainerFactory();
var result = container.GetInstance<IOneOfMany>();
Assert.That(result, Is.Not.Null);
}