Dynamic RegisterAll

Mar 5, 2015 at 4:20 PM
Hi,

I am trying to achieve the following:
I want to have a dynamic registration for all implementations of a specific interface. Dynamic in the sense that my factory is called when an IEnumerable of the service type is requested.

If it would exist, the syntax would look like this:
container.RegisterAll<IService>(() => { create and return instances });
How would I implement something like this?

Thanks,
Daniel
Coordinator
Mar 5, 2015 at 4:42 PM
Hi Daniel,

This is easier than you would expect. It's just a matter of registering the enumerable:
IEnumerable<IService> services = { some dynamicly changing collection of services }

container.RegisterAll<IService>(services);
Do note however that a collection of instances in this case is not controlled by Simple Injector (in Simple Injector jargon an 'container-uncontrolled collection) and this means that there are some limitations when it comes to using it. For instance, decorators can't be applied conditionally based on a specific implementation, and only transient decorators can be applied.
Mar 5, 2015 at 5:55 PM
Edited Mar 5, 2015 at 5:58 PM
Thanks for your quick response.
Unfortunately, this doesn't work in my scenario as I really need that code to run only after all container registrations have run.

More context: I am registering some plugins. And my plugin interfaces are basically service locators so that the plugin can choose how to create the instances.

So, that's IPlugin:
public interface IPlugin
{
    string Name { get; }
    bool CanCreateInstanceFor<T>();
    T CreateInstance<T>();
}
Registration of the plugins happens similar to the example in the documentation.

Now, assume IOptionalService is a service that can be implemented by the plugins. I would register it like so:
container.RegisterAll<IOptionalService>(
    () => container.GetAllInstances<IPlugin>()
                   .Where(x => x.CanCreateInstanceFor<IOptionalService>())
                   .Select(x => x.CreateInstance<IOptionalService>()));
Any thoughts?
Coordinator
Mar 5, 2015 at 6:16 PM
Hi Daniel,

My answer is still the same: register an IEnumerable. Please remember that an IEnumerable is in essense a stream which basically means lazy loading of instances. And Simple Injector allows it to work this way. So this is what you can do:
IEnumerable<IOptionalService> services = GetOptionalServices(container);

container.RegisterAll<IOptionalService>(services);

private static IEnumerable<IOptionalService> GetOptionalServices(Container container) {
    foreach (var plugin in container.GetAllInstances<IPlugin>()
        .Where(x => x.CanCreateInstanceFor<IOptionalService>())
        .Select(x => x.CreateInstance<IOptionalService>())) {
        yield return plugin;
    }
}
Do note the use of the yield return inside the GetOptionalServices method. This ensures that GetAllInstances is only called when the returned enumerable is actually iterated. So this ensures complete lazy behavior in the 'stream'.

Another options is to actually load the collection itself lazily, instead of loading its items lazily. This is probably what you were thinking about in the first place, and this is how to do it:
container.RegisterSingle<IEnumerable<IOptionalService>>(() =>
    container.GetAllInstances<IPlugin>()
        .Where(x => x.CanCreateInstanceFor<IOptionalService>())
        .Select(x => x.CreateInstance<IOptionalService>()));
The effect is the same as with the previous registration. As a matter of fact, in the background, RegisterAll<T> actually maps back to a RegisterSingle<IEnumerable<T>>. Even with this registration, Simple Injector is able to apply decorators. But, be warned that the Verify() method will not iterate the collection anymore; it just executes the registered delegate.
Mar 5, 2015 at 6:21 PM
Thanks - I will try.

I was asking here in the first place, because I did try container.Register<IEnumerable<IOptionalService>>(() => { the code from above }) and it didn't work - resolved IEnumerable<IOptionalService> were empty. That's why I assumed there was some other "magic" going on. That's also the reason why I refrained from using something as in the first half of your answer.
Mar 5, 2015 at 6:26 PM
OK, I just verified that the problem with the empty result set was something else.

Thanks!