views:

196

answers:

2

Hi

I am hoping either Peter or Ruben sees this question as they seem to be the go to guys regarding Ninject. I needed to create a custom provider because I have a class that takes 4 arguments. The two can be injected because they are types but the other two are configuration parameters and are integers. They refer to timeouts in milliseconds.

[SingleInstance]
MyClass
{
    ISomething something;
    IOther other;
    int timeout;
    int delay;

    [Inject]
    MyClass(ISomething something, IOther other, int timeout, int delay)
    {
        this.something = something;
        this.other = other;
        this.timeout = timeout;
        this.delay = delay;
    }
}    

I was previously relying on a factory that I had created to get the config settings for timeout and delay and to inject something and other. Now it seems to get this right I would have to create my own custom provider. Which I am okay with.

A couple of points of extra points:

  1. I know I could use injection on the parameters. But that creates a deceptive API. Objects should be returned in a ready to use state and without those 4 arguments it is not ready to use.
  2. Same argument applies to method injection.

So, my final questions:

  • Does that mean that I am in control of ensuring the single instance again or will Ninject still take care of it via the [SingleInstance] attribute?
  • Should I not just switch back to the factory I had? What do I gain from using Ninject in this case?

UPDATE: Code sample as requested

Then my provider I assume would like something like this:

class MyClassProvider : SimpleProvider<MyClass> {
protected override MyClass CreateInstance(IContext context) {
    int timeout= ConfigurationManager.AppSettings.Get("timeout");
    int delay= ConfiguraionManager.AppSettings.Get("delay");
    ISomething something = new SomethingImpl();
    IOther other = new OtherImpl();
    MyClass newOne = New MyClass(something, other, timeout, delay);
    }
}

But because I am now using a provider does that bypass ninject's mechanisms of ensuring only a single instance of the object is created so do I have to fall back on:

class MyClassProvider : SimpleProvider<MyClass> {

    protected static readonly MyClass myClassInstance;
    private static object locker = new object();

    protected override MyClass CreateInstance(IContext context) {
        if (myClassInstance == null) 
        {
            lock (locker)
            {
                int timeout = ConfigurationManager.AppSettings.Get("timeout");
                int delay = ConfiguraionManager.AppSettings.Get("delay ");
                ISomething something = new SomethingImpl();
                IOther other = new OtherImpl();
                MyClass newOne = New MyClass(something, other, timeout, delay );
            }
            return MyClassInstance
        }
        return myClassInstance
    }
}

Is there something that I am missing?

+1  A: 

To be honest I'm having trouble understanding your question - perhaps a code sample of how you create it might help (your question is generally well worded though). But here's a stab.

Not sure what you mean by SingleInstance - where is it from? (I actually wasn't aware of SingletonAttribute in v1 but see it's not in 2.0 Core). While it makes sense in some scenarions, I'd definitely not use behavior attributes on classes as a default approach (i.e., put it in the binding definitions instead).

I'd still try to use a Provider (or a Bind<T>.To* overload with a lambda as a way to write a factory in less code), especially as the instancing behavior layer can then be used on top of it and you'll be sourcing all your objects in the same manner, which is a good thing.

The other suggestion I'd make is that your bald timeout and delay parameters can probably be made first class citizens of your DI tree as e.g., a IConnectivityPolicy thing which might be used across multiple classes.

Ruben Bartelink
Hi Ruben. Thanks for the answer. I will get a code snippet for you soon. Basically what I meant is it seems that Ninject can make sure that there is only 1 instance of a particular object for you. From their documentation you could do this by either using the [SingleInstance] attribute or as you pointed or by using the Bind notation, Bind<Something>().To<SomethingElse>().Using<SingletonBehavior>. But If am creating the instances myself, does that mean I should ensure that there is only one of them. I am assuming the answer is yes. Code snippet to follow.
uriDium
@Ruben. I have another question, in particular what does the Bind<Something>.ToSelf() achieve. Where would this be useful? I can ask this as a legitimate question if you think it would help someone else. I don't see anything in SO about it yet.
uriDium
Re ToSelf(), not a bad question at all and prob worth asking. There is an AllowImplicitSelfBinding on Kernel. When that is false, one would need to use ToSelf(). The reason implicit self binding exists is that its a common thing to want to do - even if arguably using concrete classes is an antipattern not to encourage. However, its debatable whether its ideal to expose it in the kernel in this way - I believe in StructureMap they broaden it out into the notion of a Scanning Policy which can then be plugged to provide an implicit (convention over configuration) way of doing implicit binding.
Ruben Bartelink
Recent podcast talking about this stuff: http://www.code-magazine.com/codecast/index.aspx?messageid=0001bf99-1394-45c2-8949-388c673ac7a5. I guess a question is probably overkill here though I feel Mark Seeman could provide a great answer on stuff like when self binding is appropriate.
Ruben Bartelink
Re the singleton stuff. If you create them yourself, you're on the hook for everything and people need to look in more than one place for object lifetime policies. If you hook into Ninject, e.g. by doing a `Bind<T>.To*` then you can just add `.InSingleton*` and it gets managed in Ninject. In general this is much preferable as you can then rejig it as necessary rather than having it baked into your component.
Ruben Bartelink
+1  A: 

You guys have got quite a good conversation going here, but I thought I'd add an answer to help clarify things based on the original question.

The short answer is that Ninject will still ensure that you have a single instance of an object if you specify .Using<SingletonBehavior> in your binding statement, regardless of the activation mechanism -- i.e. you do not bypass Ninject's mechanisms of ensuring only a single instance of the object is created when using your own provider using .ToProvider syntax -- or .ToMethod syntax for that matter.

You should simply write your provider to supply object instances -- no other semantics are forced on the provider. So, all the typical goo required for creating singleton instances as you provided in your sample above is not required.

As an example, your provider becomes much simpler; although, I've added resolving the ISomething and IOther using the Ninject kernel to your original example:

class MyClassProvider : SimpleProvider<MyClass> {

  protected override MyClass CreateInstance(IContext context) {
    int timeout = ConfigurationManager.AppSettings.Get("timeout");
    int delay = ConfiguraionManager.AppSettings.Get("delay ");
    ISomething something = context.Kernel.Get<ISomething>();
    IOther other = context.Kernel.Get<IOther>();
    return new MyClass(something, other, timeout, delay );
  }
}

Your binding statement in your Ninject module would look like this:

public class MyModule : StandardModule {
  public override void Load() {

    Bind<IMyClass>()
      .ToProvider<MyClassProvider>()
      .Using<SingletonBehavior>();
  }
}

By using the SingletonBehavior Ninject will still give you only one instance of MyClass even though you are using your own provider.

This is pretty easy to test. You can add a DateTime property to your class, set it to DateTime.Now in the constructor and examine the objects returned by multiple calls to Get() -- they'll all have the same time even though you created the class in your provider. Next, change the Using<SingletonBehavior> to Using<TransientBehavior> and observe the difference. You'll get a new one each time, all created by your provider.

Hope this help -- by the way, not sure if I'm the Peter you were talking about, but if so, I'm flattered!

Peter Meyer
Hi Thanks for the answer. I thought that I could use the kernel to at least bing the ISomething and IOther but I thought my question was getting very long and I did it as an exercise on my own. I see now that Ninject probably wraps your provider and can still ensure SingleInstance or Transient. Very Cool.
uriDium
P.S. Yes you were the Peter I was talking about. :) ThanksP.P.S Is there any other documentation for Ninject. The website (dojo) gives you a good basic idea but it isn't complete.
uriDium
No problem! To over simplify it, when you use a provider or method for object creation you hook into the object creation pipeline. Ninject basically keeps a hashtable of objects, kind of like a cache, and looks there first to see if the object is there. If it is and you're using `SingletonBehavior` then Ninject takes the one form the table; otherwise, it starts the activation process for the object which may be it's own mechanism or your provider. Pretty cool stuff. As for docs, I used the dojo and blog posts I stumbled on. Also read a lot of the docs about Autofac which helped, too.
Peter Meyer