Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SimpleInjector - Register Object that depends on values from another registered object

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?

like image 543
Noel Avatar asked Oct 20 '22 09:10

Noel


1 Answers

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);
}
like image 148
qujck Avatar answered Oct 23 '22 01:10

qujck