Search code examples
c#azuredependency-injectionsimple-injector

Register multiple singletons with same interface but different constructor parameters


I have 2 storage (Azure) accounts lets call them src and dest and I have controllers that require access to both and I'm trying to work out how to register these 2 singletons conditionally.

This answer gave me some hope but I can't quite work it out, what I want to do is something like (appreciate RegisterSingletonConditional isn't a valid fn):

IBlobAccessClient src = new BlobAccessClient(srcConnectionString);
IBlobAccessClient dest = new BlobAccessClient(destConnectionString);

container.RegisterSingletonConditional<IBlobAccessClient>(
   src,
   c => c.Consumer.Target.Parameter.Name.Contains("src"));

container.RegisterSingletonConditional<IBlobAccessClient>(
   dest,
   c => c.Consumer.Target.Parameter.Name.Contains("dest"));

Any guidance appreciated.


Solution

  • There is a non-generic RegisterConditional overload that accepts a Registration object. You can wrap your BlobAccessClient instance in a Registration and pass it on to RegisterConditional as shown here:

    container.RegisterConditional(typeof(IBlobAccessClient),
        Lifestyle.Singleton.CreateRegistration(
            () => new BlobAccessClient(srcConnectionString), container),
        c => c.Consumer.Target.Parameter.Name.Contains("src"));
    
    container.RegisterConditional(typeof(IBlobAccessClient),
        Lifestyle.Singleton.CreateRegistration(
            () => new BlobAccessClient(destConnectionString), container),
        c => c.Consumer.Target.Parameter.Name.Contains("dest"));
    

    If this is a common pattern, you can simplify your code a bit by defining a simple extension method as follows:

    public static void RegisterConditionalInstance<TService>(
        this Container container, TService instance, Predicate<PredicateContext> predicate)
        where TService : class
    {
        container.RegisterConditional(typeof(TService),
            Lifestyle.Singleton.CreateRegistration(() => instance, container),
            predicate);
    }
    

    This allows you to reduce the previous configuration to the following:

    container.RegisterConditionalInstance<IBlobAccessClient>(
        new BlobAccessClient(srcConnectionString),
        c => c.Consumer.Target.Parameter.Name.Contains("src"));
    
    container.RegisterConditionalInstance<IBlobAccessClient>(
        new BlobAccessClient(destConnectionString),
        c => c.Consumer.Target.Parameter.Name.Contains("dest"));
    

    Optionally, you can simplify the predicate by extracting this to a simple method as well:

    private static Predicate<PredicateContext> InjectedIntoTargetNamed(string targetName) =>
        c => c.Consumer.Target.Name.Contains(targetName);
    

    This reduces the registration to the following:

    container.RegisterConditionalInstance<IBlobAccessClient>(
        new BlobAccessClient(srcConnectionString),
        InjectedIntoTargetNamed("src"));
    
    container.RegisterConditionalInstance<IBlobAccessClient>(
        new BlobAccessClient(destConnectionString),
        InjectedIntoTargetNamed("dest"));