4

I'm using Ninject 1.0 and would like to be able to inject lazy initialisation delegates into constructors. So, given the generic delegate definition:

public delegate T LazyGet<T>();

I'd simply like to bind this to IKernel.Get() so that I can pass a lazy getter into constructors, e.g.

public class Foo
{
    readonly LazyGet<Bar> getBar;

    public Foo( LazyGet<Bar> getBar )
    {
        this.getBar = getBar;
    }
}

However, I can't simply call Bind<LazyGet<T>>() because it's an open generic type. I need this to be an open generic so that I don't have to Bind all the different lazy gets to explicit types. In the above example, it should be possible to create a generic delegate dynamically that invokes IKernel.Get<T>().

How can this be achieved with Ninject 1.0?

Mike Scott
  • 12,274
  • 8
  • 40
  • 53
  • Injecting a dependency that is *explicitly* Lazy is, IMO, a Leaky Abstraction. See here for more information: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx – Mark Seemann Feb 25 '10 at 13:55
  • I know, but I'm converting an existing codebase that has a lot of statics that are highly coupled. This is just an intermediate first step to getting rid of all the static classes. – Mike Scott Feb 25 '10 at 13:58
  • Fair enough :) Anyway, I never meant my comment to be dismissive. The post I pointed to does, however, offer a way out by explicitly implementing the laziness as a Decorator. I just didn't want to offer that as an Answer, because you might get a proper Ninject answer from someone else :) – Mark Seemann Feb 25 '10 at 14:02
  • I'm not 100% clear on your use case. But you can bind open generic types via the syntax Bind(typeof(LazyGet<>)). Not sure what you're trying to bind the open generic delegate to. – Peter Meyer Feb 26 '10 at 03:46
  • Peter, I'm trying to bind it to the Get on the Ninject kernel. I don't want to create dependencies on Ninject. I'd like to pass in a delegate that can be called lazily to instantiate an object when it's required. I need to know how to write a provider or whatever that allows me to get Ninject to resolve that delegate. – Mike Scott Feb 27 '10 at 22:49
  • @MarkSeemann what do you mean by explicitly lazy? so injecting a Func is bad? I read your rebuttal but I wouldnt mind seeing some more articles. Thanks :) – AaronHS Mar 06 '12 at 12:21
  • 1
    You can find more information about this and much else in my book: http://affiliate.manning.com/idevaffiliate.php?id=1150_236 – Mark Seemann Mar 06 '12 at 14:13

3 Answers3

0

From another similar question I answered:

public class Module : NinjectModule
{
    public override void Load()
    {
        Bind(typeof(Lazy<>)).ToMethod(ctx => 
                GetType()
                    .GetMethod("GetLazyProvider", BindingFlags.Instance | BindingFlags.NonPublic)
                    .MakeGenericMethod(ctx.GenericArguments[0])
                    .Invoke(this, new object[] { ctx.Kernel }));
    }

    protected Lazy<T> GetLazyProvider<T>(IKernel kernel)
    {
        return new Lazy<T>(() => kernel.Get<T>());
    }
}
Community
  • 1
  • 1
Ed Chapel
  • 6,842
  • 3
  • 30
  • 44
0

Don't exactly understand the question, but could you use reflection? Something like:

// the type of T you want to use
Type bindType;
// the kernel you want to use
IKernel k;

// note - not compile tested
MethodInfo openGet = typeof(IKernel).GetMethod("Get`1");
MethodInfo constGet = openGet.MakeGenericMethod(bindType);

Type delegateType = typeof(LazyGet<>).MakeGenericType(bindType);
Delegate lazyGet = Delegate.CreateDelegate(delegateType, k, constGet);

Would using lazyGet allow you to do what you want? Note that you may have to call the Foo class by reflection as well, if bindType isn't known in the compile context.

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • Thanks for the code, but it's "naked" - you don't show how this is registered with Ninject. I can easily write code like you did, but how do I tell Ninject to bind that so that when it creates an instance of a type that takes a lazy delegate, it binds it to its own Get method? What I'm doing seems pretty clear to me: I want to bind the parameter to Ninject's own Get method, so that when the object requests the instance of the type, it calls Ninject's Get. – Mike Scott Apr 17 '10 at 15:26
0

I am fairly certain that the only way to do this (without some dirty reflection code) is to bind your delegate with type params. This will mean it needs to be done for each individual type you use. You could possibly use a BindingGenerator to do this in bulk, but it could get a bit ugly.

If there is a better solution (a clean one) I would love to hear it as I run into this problem from time to time.

AaronHS
  • 1,334
  • 12
  • 29