Search code examples
c#dependency-injectionsimple-injector

How to choose a separate set of services for a particular request?


I'm using Simple Injector and it seems to do well. One thing I'd really like to do and I don't know how to do, or if it's even possible, is change the list registered services, according to some parameter.

For example: if I see a URL parameter &debug=true, then I'd like to clear the list of registered services and replace them with mock implementations. this would make my selenium testing much easier.

Is this possible? Or is it completely insane?


Solution

  • Out of the box, Simple Injector prefers building object graphs that are completely known upfront. The lack of built-in features that let you build different object graphs depending on runtime variables is deliberate, since a fixed object graph is much easier to verify, which lowers the possibility that building the object graph fails at runtime (after .Verify() was called successfully). Besides verifiability, it improves performance as well.

    But although nothing is built-in, there are still many ways to switch services or whole object graphs dynamically based on runtime conditions. Three possibilities that come to my mind are using context based injection, runtime decorators and registering a simple delegate.

    Here's an example of a delegate registration:

    container.Register<IMailSender>(() => IsDebug(HttpContext.Current)
        ? container.GetInstance<MailSender>()
        : container.GetInstance<MockSender>());
    

    But even though it is possible, you might want to consider a different approach. For instance, instead of using a runtime value, use a configuration value. This seems very reasonable in your case, because would you ever want to be able to use your production environment for web testing at the same time? Seems very unlikely to me. Especially since it is really easy to deploy a second IIS web application on the same server.

    So instead, you can create a special deployment of your application for your Selenium framework and this deployment contains a value in the web.config with <add key="Debug" value="True" />, or something similar. Now within you composition root (the place where you bootstrap your container ), you can simply do this:

    bool debug = bool.Parse(ConfigurationManager.AppSettings["Debug"]);
    
    if (debug) {
        container.Register<IMailSender, MockSender>();
        // more registrations here
    } else {
        container.Register<IMailSender, MailSender>();
        // more registrations here
    }
    

    This allows the object graph to be fixed at runtime, while still being able to change things during deployment.