2

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?

Steven
  • 166,672
  • 24
  • 332
  • 435
amirmonshi
  • 649
  • 5
  • 19

2 Answers2

9

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

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • Thank you very much. You not only gave very constructive hints about the design but also answered my actual question. I think even if this is not a useful choice, at least if some people want to use it in a right way, they will find your example useful. I really appreciate it! about convensions-based binding I am reflecting what I have read from ninject wiki here https://github.com/ninject/ninject/wiki/Conventions-Based-Binding probably I have misunderstood though. – amirmonshi Mar 10 '11 at 13:57
  • 1
    @amirmonshi: Thanks for the ack and followup. Now that you point it out, it's no wonder you came in calling it convention based binding. The article will receive some work soon - as it notes, it's a bit confused - For me there's conventions (typically CoC a la ruby, based on Names) vs contextual (i.e., with attributes and other richer context, with the focus on static information). I think some of the other articles cover Contextual bindings. Hopefully I'll get around to it soon and I can remove some of the confusion. – Ruben Bartelink Mar 10 '11 at 14:08
  • @amirmonshi: I also edited out some references to Conditional binding and replaced with Contextual. That was me playing fast and loose with terminology. And dont forget to follow the seealso I posted on the question – Ruben Bartelink Mar 10 '11 at 14:14
6

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?

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Steven
  • 166,672
  • 24
  • 332
  • 435
  • after reformatting it, I was able to read it, and it was good - I agree with your central point of the OP's specific example not being a good idea +1! – Ruben Bartelink Mar 10 '11 at 16:44
  • @Ruben: Thanks for the + and thank for reformatting. I had no idea how to include the code within a list item. Now I see 4 extra spaces do the trick. You already had my +, so I can't give you more ;-) – Steven Mar 10 '11 at 18:19