How does Unity.Resolve know which constructor to use?
Asked Answered
S

2

36

Given a class with several constructors - how can I tell Resolve which constructor to use?

Consider the following example class:

public class Foo
{
    public Foo() { }
    public Foo(IBar bar)
    {
        Bar = bar;
    }
    public Foo(string name, IBar bar)
    {
        Bar = bar;
        Name = name;
    }
    public IBar Bar { get; set; }        
    public string Name { get; set; }
}

If I want to create an object of type Foo using Resolve how will Resolve know which constructor to use? And how can I tell it to use the right one? Let's say I have a container with an IBar registered - will it understand that it should favor the constructor taking IBar? And if I specify a string too - will it use the (string, IBar) constructor?

Foo foo = unityContainer.Resolve<Foo>(); 

And please ignore the fact that it would probably be easier if the class just had a single constructor...

Stadium answered 18/3, 2010 at 13:29 Comment(0)
R
63

When a target class contains more than one constructor, Unity will use the one that has the InjectionConstructor attribute applied. If there is more than one constructor, and none carries the InjectionConstructor attribute, Unity will use the constructor with the most parameters. If there is more than one such constructor (more than one of the “longest” with the same number of parameters), Unity will raise an exception.

Taken from link text

Rocha answered 18/3, 2010 at 13:33 Comment(3)
Exactly what I was looking for! I just decorate the constructor I want to be used with [InjectionConstructor]Interradial
That's the answer which would save a weekend. Well defined.Suppurate
I have a situation where Unity doesn't choose the constructor with the most parameters. There are two constructors, one with one parameter, the other one with 4 parameters. It chooses the one with one parameter, why is that? Any ideas or thoughts or where should I check?Wince
L
35

When you register the type, you can specify which constructor to use like this:

container.RegisterType<Foo>(
    new InjectionConstructor(
        new ResolvedParameter<IBar>()));

The above code is from memory, but that's the general principle. In this case I've picked the constructor that takes a single parameter of type IBar.

And please ignore the fact that it would probably be easier if the class just had a single constructor...

I can't ignore this. When it comes to Constructor Injection, ambiguity is a design smell. You are basically saying: I don't really know if I care about this dependency or not.

Sure, Unity is likely to figure it out for you, but then you would be relying on a specific container behavior instead of properly designing your API. Other containers may have a different behavior, so if you ever choose to migrate from Unity to a better container, subtle bugs may occur.

It's much safer to write your code in a DI-friendly, but container-agnostic manner.

Leg answered 18/3, 2010 at 14:9 Comment(4)
Thanks! But do I really need to register the type in the container? I will be using the container to resolve the instances of Foo, but I don't need Foo to be in the container. But I should still use RegisterType? Or just because I have this specific need of telling it which constructor to use?Stadium
Yes, and no. As ozczecho quoted, Unity uses a heuristic approach to pick a constructor in the face of ambiguity (it picks the constructor with most parameters). If you need it to deviate from this algorithm, you must explicitly tell it so. One way is to apply the InjectionConstructor attribute, and another is by registering it in the container. I personally prefer registering the type in the container, because that lets me keep my code container-agnostic. It also allows different container configurations, if applicable.Leg
FWIW, Unity's ability to resolve concrete types even if they are not already registered in the container is a feature particular to it. Some containers support that feature, while others don't.Leg
+1 you are right, pointing out that ambiguity is a design smell made me realise I should use parameter injection rather than constructor injection. The only reason I was trying for two constructors was for testability.Mchenry

© 2022 - 2024 — McMap. All rights reserved.