2

I have structure like following:

static void Main(string[] args) {
    var container = new UnityContainer();
    container.RegisterType<IBaseService, ServiceA>("a");
    container.RegisterType<IBaseService, ServiceB>("b");
    container.RegisterType<IWorker, Worker>();

    var runner = container.Resolve<Runner>();
    /* ... */
}

public class Runner {
    public Runner(IWorker worker) {}

}

public class Worker : IWorker {
    public Worker(IBaseService a, IBaseService b) { /* ... */ }
}

public class ServiceA : IBaseService {}
public class ServiceB : IBaseService {}

I know I may set additional interface for ServiceA like IServiceA (the same for ServiceB - IServiceB) and register them in container. But is it possible to resolve instance of Worker with two services having the same contract IBaseService (first param should get ServiceA, second - ServiceB) or it would led to sort of ambiguity?

2
  • Does the order matter or do you just want all registered services? In the latter case public Worker(IBaseService[] allServices) should work. Commented Feb 14, 2016 at 21:18
  • Since each service should be used in Worker via constructor like this._serviceA = a; this._serviceB = b; I guess I need ordered services Commented Feb 15, 2016 at 6:38

1 Answer 1

3

If you try and run the code as posted you will receive an exception:

The current type, IBaseService, is an interface and cannot be constructed. Are you missing a type mapping?

This is because there is no default registration (default meaning with no name) registered in the container for IBaseService. There are only two named interface mappings registered.

There are few ways to handle this situation. One way, as mentioned by @Haukinger, is to inject all IBaseService implementations into the object.

So you could declare your class like this:

public class Worker2 : IWorker
{
    public Worker2(IEnumerable<IBaseService> baseServices)
    {
        /* ... */
    }
}

and then register with the container like this:

container.RegisterType(typeof(IEnumerable<IBaseService>), typeof(IBaseService[]));

This maps an IEnumerable<IBaseService> to an array of IBaseService which works because when Unity sees an array it will inject use all named registrations and inject those into the object. You could also use an array directly without the IEnumerable mapping.

This approach can be useful but more often than not you want to know which type is being injected (e.g. a factory where the correct implementation must be chosen at runtime based on some other criteria).

Another approach is to configure Unity to inject the correct named registrations into the Worker:

container.RegisterType<IWorker, Worker>(
    new InjectionConstructor(
        new ResolvedParameter<IBaseService>("a"), 
        new ResolvedParameter<IBaseService>("b")));

In the above case, we tell Unity what constructor to use and which implementation to inject.

Another approach is to use an InjectionFactory:

container.RegisterType<IWorker, Worker>(
    new InjectionFactory(
        c => new Worker(c.Resolve<IBaseService>("a"), c.Resolve<IBaseService>("b"))
        ));

One benefit of using the InjectionFactory is that because we are newing-up the Worker object we get compiler checking of the constructor. e.g. if the Worker constructor was changed to add a new parameter then there would be a compilation error. In constrast, the InjectionConstructor approach would compile but would generate a runtime error System.InvalidOperationException: The type Worker does not have a constructor that takes the parameters (IBaseService, IBaseService).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for explanation! It definitely helps me.
@Randy How would you use the first approach when the IBaseService is a of a generic type? I'm using the code below but it's giving me compiled error: services.AddScoped(typeof(IEnumerable<IDownloadData<>>), typeof(IDownloadData<>[]));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.