I'm building a plugin system for an e-commerce project with Simple Injector. I'm using RegisterAll to register all implementation of a IPaymentProvider and of a IResourceRegistrar (and in the fure more).

But this creates a new instance each time. Here is it suggested to use RegisterSingle on each type. But how to achieve this in this case?

private static void RegisterMultipleUnderOneInterface(
    Container container, string name)
{
    IEnumerable<Type> pluginRegistrations =
        from dll in finder.Assemblies
        from type in dll.GetExportedTypes()
        where type.GetInterfaces().Any(i => i.Name == name)
        where !type.IsAbstract
        where !type.IsGenericTypeDefinition
        select type;

    if (pluginRegistrations.Any())
    {
        var @interface =
            pluginRegistrations.ElementAt(0).GetInterfaces()
            .First(i => i.Name == name);

        foreach (Type type in pluginRegistrations)
        {
            // HERE: register the type single somehow. 
        }

        container.RegisterAll(@interface, pluginRegistrations);
    }
} 

container.RegisterSingle(type) does not work, as the types inherent from the same interface (IPaymentProvider or IResourceRegistrar). The IPaymentProvider implementing classes have constructors without parameters, the IResourceRegistrar with parameters.

I don't want to do something like this, it rather defeats the purpose of a IoC container

var constructor = type.GetConstructors()[0];

switch (name)
{
    case "IResourceRegistrar":
        container.RegisterSingle(type, () =>
        {
            return constructor.Invoke(new object[
            {
                container.GetInstance<ILanguageService>()});
            });
        break;
    case "IPaymentProvider":
    default:
        container.RegisterSingle(type, () =>
        {
            return constructor.Invoke(new object[] { });
        });
        break;
}

How to register these as singleton without the ugly switch?

有帮助吗?

解决方案

Perhaps I misunderstand, but RegisterSingle should work. You should be able to do this:

var types = ...

container.RegisterAll<IInterface>(types);

foreach (var type in types)
{
    container.RegisterSingle(type, type);
}

UPDATE:

So what you are trying to do is to automate the following configuration:

// A, B, C and D implement both I1 and I2.
container.RegisterSingle<A>();
container.RegisterSingle<B>();
container.RegisterSingle<C>();
container.RegisterSingle<D>();

container.RegisterAll<I1>(typeof(A), typeof(B), typeof(C), typeof(D));
container.RegisterAll<I2>(typeof(A), typeof(B), typeof(C), typeof(D));

This would typically be the way to automate this. So do four steps:

  1. Find all types to register.
  2. Register the found types as singleton.
  3. Register the list of types as collection for I1.
  4. Register the list of types as collection for I2.

This would look like this:

// using SimpleInjector.Extensions;

Type[] singletons = FindAllTypesToRegister();

foreach (Type type in singletons)
{
    container.RegisterSingle(type, type);
}

container.RegisterAll(typeof(I1), singletons);
container.RegisterAll(typeof(I2), singletons);

However, since you are trying to split this into two steps and create one generic method that can handle each step, you will have to ignore when a concrete singleton type has already been registered. You can either do this by:

  • Ignoring the registration by catching the exception thrown from RegisterSingle.
  • Override an existing registration by setting container.Options.AllowOverridingRegistrations = true before calling RegisterSingle (disabling it afterwards would be safest).
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top