Register multiple singletons with same interface b

2019-02-19 13:49发布

问题:

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.

回答1:

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"));


回答2:

I would suggest defining two new interfaces:

public interface IBlobAccessClientSource : IBlobAccessClient
{ }

public interface IBlobAccessClientDestination : IBlobAccessClient
{ }

And then registering them both individually (in whatever way that SimpleInject supports).

Then in your consumers, inject in IBlobAccessClientSource and / or IBlobAccessClientDestination as needed. This will ensure that there are two singleton registrations - one for each of the two interfaces.