Matt Burland
Matt Burland

Reputation: 45135

Inject a singleton with parameters

Using Ninject, I have an interface that I want to bind to single instance of a concrete implementation. For example:

public interface IFoo { //... }
public class Foo { //... }

Now normally, I'd just bind something like this like so:

kernel.Bind<IFoo>().To<Foo>().InSingletonScope();

But, I need to add parameters to the constructor for Foo. Normally, again, that wouldn't be too much of a problem (I think):

kernel.Bind<IFoo>()
    .To<Foo>()
    .InSingletonScope()
    .WithConstructorArgument("bar", myBar);

Now the problem is that I can't know the value of myBar at the time I set up all my bindings. I need to defer that until the first time I need an IFoo (and note, in reality I have several arguments to pass). So what I need is a singleton, that will be lazy initialized on first use and only gets arguments at that point.

What's the best way to approach this? I'm assuming something with Factory is probably the solution, but I don't quite see the right way to do this. I don't want to create a new Foo every time.

Upvotes: 2

Views: 535

Answers (3)

Andrew Savinykh
Andrew Savinykh

Reputation: 26280

Given the other two answers, I could be completely missing the point of the question, but why would not something as simple as this work for you:

kernel.Bind<IFoo>().ToMethod(x => CreateFoo()).InSingletonScope();

CreateFoo will be responsible for constructing your single object with whatever set of parameters that you need. By the time CreateFoo is called you should already know what the parameters are.

Upvotes: 0

treze
treze

Reputation: 3289

You can use the Factory extension.

public interface IFooFactory
{
    IFoo CreateFoo(string bar);
    IFoo CreateFoo();
}

public interface IFoo
{
    string Bar { get; set; }
}

public class Foo : IFoo
{
    public string Bar { get; set; }

    public Foo(string bar)
    {
        Bar = bar;
    }
}

kernel.Bind<IFoo>().To<Foo>().InSingletonScope();
kernel.Bind<IFooFactory>().ToFactory();

IFoo foo1 = fooFactory.CreateFoo("myBar");
IFoo foo2 = fooFactory.CreateFoo("myDifferentBar"); // value is basically ignored here
IFoo foo3 = fooFactory.CreateFoo();

This will always return the same instance of Foo. Of course if you call the paremeterless method first it will result in an exception.

Upvotes: 1

Ewan
Ewan

Reputation: 1285

As in my comment above. the real problem is that you may not have the construction parameters when you need Foo. In this pattern you can Bind all your interfaces as you please and call IInitialiser.Initialise when you are ready (obvs you need to keep a reference or make it static).

Foo will throw an exception if you call it before its been properly set up

IFoo remains unchanged

IInitialiser implementations can be tweaked to poll a DB or respond to events or whatever suits your late configuration senario best

using System;

namespace UnitTestProject3
{
    public interface IFoo
    {
        int GetAllTheFoo();
    }

    public interface IInitialiser
    {
        void Initialise(int x);

        int GetX();

        bool IsReady { get; }
    }

    public class Foo : IFoo
    {
        private bool isInitalised;
        private int x;
        private IInitialiser i;
        public Foo(IInitialiser i)
        {
            this.isInitalised = false;
            this.i = i;
        }

        protected void Init()
        {
            if (this.isInitalised)
            {
                return;
            }
            else if (i.IsReady)
            {
                x = i.GetX();
                this.isInitalised = true;
                return;
            }
            else
            {
                throw new Exception("you have not set x");
            }
        }

        public int GetAllTheFoo()
        {
            Init();
            return x;
        }
    }

}

Upvotes: 1

Related Questions