views:

280

answers:

1

I'm having something like this:

class Root
{
    public Root(IDependency dep)
    {}
}
class Dependency:IDependency
{
    public Dependency(int val)
    {}
}

And I'm trying to obtain a reference to Root using ninject. So i configure it like this

var module = new InlineModule(mod => mod.Bind<IDependency().To<Dependency>());

var kernel = new StandardKernel(module);

I'd like to inject into Dependency some 'val' value that is known only at the moment of obtaining the Root reference from ninject.

What i'd like to do is something like this:

Kernel.Instance.Get<Root>(With.Parameters.ConstructorArgument("val", 12));

Is something like this possible using ninject 1.0?

+2  A: 

The Parameters.ConstructorArgument in the context only goes one leve deep by default.

One way of passing parameters down multiple levels is by using a ContextParameter, but something then needs to grab that and say - and now we're going to use that as a ConstructorArgument in this case. One such construct is Providers. See this dojo page for details of Providers

So you can do:

    class DependencyProvider : SimpleProvider<Dependency>
    {
        protected override Dependency CreateInstance( IContext context )
        {
            return new Dependency( (int)context.ParentContext.Parameters.GetOne<ContextVariableParameter>( "masterVal" ).Value );
        }
    }

    public static void Main()
    {
        var module = new InlineModule(
            mod => mod.Bind<IDependency>().ToProvider( new DependencyProvider() )
        );

        var kernel = new StandardKernel( new[  ] {module} );

        Root root = kernel.Get<Root>( With.Parameters.ContextVariable( "masterVal", 12 ) ); 
    }

Or you can manage it as follows:

    class RootProvider : SimpleProvider<Root>
    {
        protected override Root CreateInstance( IContext context )
        {
            return new Root( context.Kernel.Get<Dependency>( With.Parameters.ConstructorArgument("val", ( int )context.Parameters.GetOne<ContextVariableParameter>("masterVal").Value )));
        }
    }

    public static void Main()
    {
        var module = new InlineModule(
            mod => mod.Bind<IDependency>().To<Dependency>(), // Optional if ImplictSelfBinding is on
            mod => mod.Bind<Root>().ToProvider( new RootProvider() )
        );

        var kernel = new StandardKernel( new[] {module} );

        Root root = kernel.Get<Root>( With.Parameters.ContextVariable( "masterVal", 12 ) ); 
    }

While you're thinking about this, consider the points I make in this point re separating the concerns if configuration from object binding in this response.

Ruben Bartelink
I think i could do it a bit more simply like: Bind<IDependency>().To<Dependency>().WithConstructorArgument("val", (IContext cont) => cont.ParentContext.Parameters.Get<ConstructorArgumentParameter>("val"));But i don't like using the ParentContext. :( It
Silviu
@Silviu: Try it - the shorter the better. It seemed to make sense to me as as doign directly what you were asking, but I couldnt get it to work for me in the context I was trying it.
Ruben Bartelink
@Silviu: Did you get doing it as part of the `Bind` work out? Iff so, if you want to chuck something that works into the comments, I'll put it into the examples.
Ruben Bartelink
Sorry for the late response.I tried with the parent context. It worked. Not very proud of it. :DProbably my point of view's fault but when I'm defining a binding I'm telling Ninject to use some key value pair. And at that moment for some reason i expected to consider those values for the entire chain of dependencies because that was the context being used. In fact i considered that the context represents the medium based on which Ninject builds things. So once defined for some class it should use that context for all deps. Each dep's binding adding to the same context.
Silviu
I really don't like that parent thing. Feels like I need to know at the binding level too much stuff about the hierarchy of dependencies. I mean, what if i put another layer between the Root and the Dependency? Then I'll have to modify the binding of the dependency to context.ParentContext.ParentContext and that is some seriously ugly coupling.
Silviu
I agree with your concerns as you put it. The main reason I feel it is set up as it is is that you definitely dont want to have a global context - in the same way that you're pushed towards sticking things in modules, any passing of stuff down chains during injection should be in a managed way in terms of knowing what you're doing. You're right to keep your eyes open when doing stuff in this space. In general, you only want to get into context like this as little as possible and use higher level constructs like having one clear interface with one clear thing to bind to where possible.
Ruben Bartelink
Is what you're looking for in the direction of names and/or can you re-angle your requirements in that direction? See http://stackoverflow.com/questions/2099092/using-ninject-ioc-to-replace-a-factory/2112070#2112070
Ruben Bartelink