amirmonshi
amirmonshi

Reputation: 659

Ninject Contextual binding

I mostly have been using basic functionalities of Ninject. Therefore, this question might be too obvious. If so please excuse me. Anyways, I have the following in a module:

Bind<Double>().ToConstant(TimeSpan.FromSeconds(10).TotalMilliseconds).Only(When.Context.Target.Name.AsString=="A");
Bind<Double>().ToConstant(TimeSpan.FromHours(1).TotalMilliseconds).Only(When.Context.Target.Name.AsString=="B");
Bind<Double>().ToConstant(TimeSpan.FromMinutes(5).TotalMilliseconds).Only(When.Context.Target.Name.AsString=="C");

Now, the question is how can I use kernel.get<Double>() to resolve to any one of the above bindings that I want?

Upvotes: 2

Views: 2090

Answers (2)

Ruben Bartelink
Ruben Bartelink

Reputation: 61795

Firstly, can we keep Conventions Based Binding to mean https://github.com/ninject/ninject.extensions.conventions please (as opposed to explicitly hardwiring bindings as you're doing in your example) -- I've edited the question title to reflect this.

Now, to answer your actual question. You seem to want to set up contextual bindings in order to be able to feed in appropriate values in different contexts. I'd suggest that editing in a better exanmple of what you actually are trying to achieve might have got/get you a better answer.

The first problem is that you're using v1 syntax. Here's an example of what you're trying to achieve in v2 syntax:

class ContextualBindingsExample
{
    internal class A
    {
        public A( double x )
        {
            X = x;
        }

        public double X { get; private set; }
    }

    internal class B
    {
        public B( double y )
        {
            Y = y;
        }

        public double Y { get; private set; }
    }

    [Fact]
    public static ContextualBindingAllowsOneToFilterWhenInjectingIntoRoots()
    {
        var k = new StandardKernel();
        k.Bind<double>().ToConstant( 1 ).WhenInjectedInto<A>();
        k.Bind<double>().ToConstant( 2 ).When( request => request.Target.Name == "y" );
        Assert.Equal( k.Get<B>().Y, 2 );
        Assert.Equal( k.Get<A>().X, 1 );
    }
}

The above approach (trying to Get something that matches your conditional situation) is typically what you need. If not, you can synthesize it as follows. NB this is not a normal thing to want to do and you're probably doing something wrong if you do want to do this.

What you might also be looking for is a way to stash stuff in your container and then pull it out based on filter criteria. That goes like this:

    [Fact]
    public static void BindingMetadataShouldAllowContextualFiltering()
    {
        var k = new StandardKernel();
        k.Bind<double>().ToConstant( 2 ).WithMetadata( "a", "b" );
        k.Bind<double>().ToConstant( 30 ).WithMetadata( "b", 2 );
        Assert.Equal( k.Get<double>( metadata => metadata.Get<string>( "a" ) == "b" ), 2 );
        Assert.Equal( k.Get<double>( metadata => metadata.Get<int>( "b" ) == 2 ), 30 );
    }

Note that this isn't typically a good idea, but again, a better question will get you a better more contextual answer there...

Now, I recommend going to read @Mark Seemann's top rated answers around here to get key principles so you dont end up in the weeds trying to understand low level DI tool trickery like this again!

EDIT: Note that https://github.com/ninject/ninject/wiki/Contextual-Binding has been significantly updated since this question (and answer) where written

Upvotes: 9

Steven
Steven

Reputation: 172606

While such configuration might work, it is particularly fragile and will lead to hard to understand code and hard to understand DI configuration. Convention over configuration is fine, but don't try to inject anything else than services. A double is not a service, it's a value.

Here are three other approaches you might want to consider:

  1. Define one or multiple services with clear names that know how to return those values. For instance:

    public interface ISystemClock
    {
        DateTime Now { get; }
    }
    

    You can inject such service.

  2. Define an interface for the application's configuration:

    public interface IMyAppsConfiguration
    {
        double Value1 { get; }
        double Value2 { get; }
        double Value3 { get; }
    }
    
  3. Register the classes directly using Ninject.

What's the best answer is hard to tell, because your question doesn't reveal much about what you are actually trying to inject. You are trying to inject doubles, but what do these doubles actually represent?

Upvotes: 6

Related Questions