Will
Will

Reputation: 321

How to change dependency registration at run time using simple injector?

I'm using the Simple Injector IoC framework, and I would like to be able to change the dependency registration at run time. For example, I have two implementations, A and B, of interface I. Implementation A is registered at app start, but depending on some flag which can change during runtime, I would like to switch the implementation. We are currently doing this the OnActionExecuting event of our BaseController, which all of our controllers inherit from. Here is the sample code of what I am trying to do.

protected override void OnActionExecuting(
    ActionExecutingContext filterContext)
{
    if (IsRuntimeFlag)
    {
        // check current implementation type and 
        // change implementation to A
    }
    else
    {
        // check current implementation type and 
        // change implementation to B
    }

    base.OnActionExecuting(filterContext);
}

Thanks in advance for your help.

Upvotes: 6

Views: 3495

Answers (1)

Steven
Steven

Reputation: 172606

In case IsRuntimeFlag is a configuration value (thus cannot change during the lifetime of the application), you can make the registration as follows:

if (IsRuntimeFlag)
{
    container.Register<I, A>();
}
else
{
    container.Register<I, B>();
}

or equally:

container.Register(typeof(I), IsRuntimeFlag ? typeof(A) : typeof(B));

In case the value can change during the lifetime of the application a proxy or composite that deals with dispatching to the right instance is the right solution:

public sealed class RuntimeFlagIComposite : I
{
    private readonly A a;
    private readonly B b;

    public RuntimeFlagIComposite(A a, B b) {
        this.a = a;
        this.b = b;
    }

    void I.Method() => this.Instance.Method();

    private I Instance => IsRuntimeFlag ? this.a : this.b;
}

Because the composite directly depends on A and B, you can simply register it as follows:

container.Register<I, RuntimeFlagIComposite>();

// Register A and B with their required lifestyles
container.Register<A>(Lifestyle.Singleton);
container.Register<B>(Lifestyle.Transient);

You can also let your composite depend on the I abstraction itself instead of the concrete A and B implementations:

public class RuntimeFlagIComposite : I
{
    private I a;
    private I b;

    public RuntimeFlagIComposite(I a, I b)
    {
        this.a = a;
        this.b = b;
    }
}

Depending on the I abstraction makes this class more flexible and more testable. It does mean however, that you need to register it a little bit different. This can be done using RegisterConditional. Here's an example:

container.Register<I, RuntimeFlagIComposite>();

container.RegisterConditional<I, A>(c => c.Consumer.Target.Name == "a");
container.RegisterConditional<I, B>(c => c.Consumer.Target.Name == "b");

Upvotes: 11

Related Questions