Constructor Injection with TinyIoC
Asked Answered
C

1

5

I have just changed from Ninject to TinyIoC for dependency injection and I'm having trouble with constructor injection.

I have managed to simplify it down to this snippet:

public interface IBar { } 

public class Foo
{
    public Foo(IBar bar) { }
}

public class Bar : IBar
{
    public Bar(string value) { }
}

class Program
{
    static void Main(string[] args)
    {
        var container = TinyIoCContainer.Current;

        string value = "test";
        container.Register<IBar, Bar>().UsingConstructor(() => new Bar(value));

        var foo = container.Resolve<Foo>();
        Console.WriteLine(foo.GetType());
    }
}

which causes a TinyIoCResolutionException to be thrown with:

"Unable to resolve type: TinyIoCTestApp.Foo"

and inside that exception is a chain of inner exceptions:

"Unable to resolve type: TinyIoCTestApp.Bar"
"Unable to resolve type: System.String"
"Unable to resolve type: System.Char[]"
"Value cannot be null.\r\nParameter name: key"

Is there something wrong with the way I'm using the constructor injection? I realize I could call

container.Register<IBar, Bar>(new Bar(value));

and that does indeed work, however the result is a global instance of Bar which is not what I'm after.

Any ideas?

Chicken answered 8/2, 2012 at 23:1 Comment(4)
Also: I'm using TinyIoC from Github (github.com/grumpydev/TinyIoC)Chicken
Funny, the rationale behind TinyIoC has much in common with that of the Simple Injector.Plowboy
@Plowboy and we're both called Steven too .. spooky :-PRupee
@Steven: Nothing but good can come out of that ;-)Plowboy
P
11

I'm not familiar with TinyIOC, but I think I can answer your question.

The UsingConstructor registers a lambda that points at a constructor (the ctor(string)) that TinyIOC will use to do automatic constructor injection into. TinyIOC will analyse the constructor arguments, finds an argument of type System.String and tries to resolve that type. Since you haven't registered System.String explicitly (which you shouldn't btw), resolving IBar (and thus Foo) fails.

The incorrect assumption you made is that TinyIOC will execute your () => new Bar(value)) lambda, which it will not. If you look at the UsingConstructor method you will propable see that it takes an Expression<Func<T>> instead of a Func<T>.

The thing you want, is to register a factory delegate that does the creation. I expect TinyIOC to contain a method for this. It might look something like this:

container.Register<IBar>(() => new Bar(value));
Plowboy answered 9/2, 2012 at 8:48 Comment(6)
Yep, UsingConstructor is a way to select which constructor to use and for 99% of the time isn't necessary. What you want is the Register overload that takes Func<TinyIoCContainer, NamedParameterOverloads, object> and supply a simple factory to return your new Bar.Rupee
I agree. UsingConstructor is only useful when your type has multiple constructors, which should in general be avoided when doing DI. I even left a constructor selection feature of of Simple Injector because of that.Plowboy
Well, multiple constructors are generally fine with TinyIoC because it takes the "greediest constructor I can successfully resolve" approach, but there are occasions where you want to override that.Rupee
Interesting. I force Simple Injector users to have a single constructor on types that are auto-wired, because of two reasons. First of all, the general advice with DI is to let a type have a single definition of the dependencies it needs. If you have multiple constructors, you have multiple definitions. But second: all containers use a different overload resolution. Some containers are greedy, while others aren't. Forcing users to have a single constructor makes it easier to migrate to another DI framework when needed.Plowboy
@Stevens: Whoa, I'm seeing double! :) I changed the Register call to use the overload as suggested and all is well now. Thanks guys!Chicken
@AndrewG: Building on what you mentioned, I used container.Register<IBar, Bar>(new Bar(myParameter));Dorweiler

© 2022 - 2024 — McMap. All rights reserved.