9

I have a problem which seems very similar to the one described in http://markmail.org/message/6rlrzkgyx3pspmnf which is about the singleton actually creating more than a single instance if you're accessing it using different service types.

I'm using the latest release of Ninject 2 for Compact Framework and the exact issue I'm having is that if I bind the same provider method to:

Func<Service> serviceCreator = () => new Service(false);
kernel.Bind<IService>().ToMethod(serviceCreator).InSingletonScope();
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope();

It seems to be creating 2 instances of Service if I resolve both as IService and Service.

This causes a circular dependency exception when resolving Service.

Is this by design, or is it a bug?

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Michał Drozdowicz
  • 1,232
  • 11
  • 30
  • BTW I believe there are some inconsistencies being cleaned up in the 2.3 and 2.4 releases of Ninject around making sure that stuff you reuse in this manner only gets activated and/or cleaned up once – Ruben Bartelink Feb 18 '11 at 16:12
  • See V3-specific answer: http://stackoverflow.com/questions/10206049/ninject-is-it-possible-to-bind-different-interfaces-to-the-same-instance-of-a-c – Ruben Bartelink Sep 12 '12 at 08:53
  • related: http://stackoverflow.com/questions/8303661/ninject-binding-interface-to-interface/8303826#comment16639462_8303826 – Ruben Bartelink Sep 12 '12 at 10:12

3 Answers3

11

In V3, there is finally a solution for this in the shape of new overloads on Bind, see this related: question.


If you want the singleton to be shared, you need to change your second Bind to:

kernel.Bind<Service>().ToMethod(()=>kernel.Get<IService>()).InSingletonScope();

Re circular references and confusion etc. Internally the implicit self-binding will add an implicit binding registration for Service. You should post the exception.

EDIT: Re your comment. If you do it like this:

Func<Service> serviceCreator = () => new Service(false);
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope();
kernel.Bind<IService>().ToMethod(()=>kernel.Get<Service>()).InSingletonScope();

Then no implicit Class Self Binding gets generated when IService gets Resolved - it uses the existing one.

There was another Q here on SO in recent weeks someone was doing this type of thing but was running into an issue with IInitializable - that example would have the correct ordering but the one above makes sense based on my reading of the source and the way in which it generates the implicit class self-bindings.

Community
  • 1
  • 1
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • This actually causes a stack overflow in my scenario. When I have time, I'll try to isolate the problem and post a minimalistic example of my scenario. – Michał Drozdowicz Jun 30 '10 at 11:51
  • Heh, seems I applied your first suggestion wrong - I used Get instead of Get. D'oh. ;) – Michał Drozdowicz Jul 06 '10 at 10:08
  • BTW read the comment I just stuck on the question if you're finding any wackiness around multiple bindings for singletons - there are some improvements to the handling in Ninject on the way – Ruben Bartelink Feb 18 '11 at 16:13
6

By the way, Ninject 3 allows this syntax:

kernel.Bind<IService, Service>().ToMethod(serviceCreator).InSingletonScope();

Or, similarly:

kernel.Bind(typeof(IService), typeof(Service)).ToMethod(serviceCreator).InSingletonScope();

This latter approach works better if you have many services, or if you discovered the services dynamically at runtime (you can pass the params-style arguments as an array directly).

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • 2
    This is the way to go, but was probably unavailable when the question was originally asked. – BatteryBackupUnit Jan 29 '14 at 12:16
  • @BatteryBackupUnit: regarding your edit: that doesn't look like valid C# syntax to me. Can you clarify it, or possibly add it as your own answer? – StriplingWarrior Oct 19 '17 at 15:30
  • 2
    sorry, was a bit too eager i guess. for Bind, there is an overload `Bind(params Type[] services)` that accepts (almost) any number of types. The `Bind` maxes out at 4 types. So the other overload is beneficial either if you've got more than 4 types or when you use reflection to get the types. Could you maybe add this to your own answer with a better "wording"? It would make the answer complete IMHO. – BatteryBackupUnit Oct 20 '17 at 06:00
4

We used Ruben's method in our project, but found that it wasn't intuitive why you were going back to the Kernel in your binding. I created an extension method and helper class (below) so you can do this:

kernel.Bind<IService>().ToExisting().Singleton<Service>();

That seemed to express the intent more clearly to me.

public static class DIExtensions
{
    public static ToExistingSingletonSyntax<T> ToExisting<T>(this IBindingToSyntax<T> binding)
    {
        return new ToExistingSingletonSyntax<T>(binding);
    }
}

// Had to create this intermediate class because we have two type parameters -- the interface and the implementation,
// but we want the compiler to infer the interface type and we supply the implementation type.  C# can't do that.
public class ToExistingSingletonSyntax<T>
{
    internal ToExistingSingletonSyntax(IBindingToSyntax<T> binding)
    {
        _binding = binding;
    }

    public IBindingNamedWithOrOnSyntax<T> Singleton<TImplementation>() where TImplementation : T
    {
        return _binding.ToMethod(ctx => ctx.Kernel.Get<TImplementation>()).InSingletonScope();
    }


    private IBindingToSyntax<T> _binding;
}
Eddie Deyo
  • 5,200
  • 8
  • 35
  • 35
  • Nice example. BTW I believe there's a new `Bind<>.ToBinding` or something of that ilk bult in, or in an extension / in a @Remo Gloor blog post on the way that productizes a mechanism similar to yours. – Ruben Bartelink Jul 31 '11 at 21:24
  • @RubenBartelink is correct: https://github.com/ninject/ninject.extensions.contextpreservation – Filip Cornelissen Oct 13 '11 at 14:05
  • Link for post I was alluding to in earlier comment http://www.planetgeek.ch/2011/12/30/new-features-and-changes-of-ninject-3-0/ – Ruben Bartelink Sep 12 '12 at 08:33