2

I have a PriorityQueue that takes a Func as construction parameter.

public PriorityQueue(ISomeOtherInjectedThing other, Func<T, T, bool> cmp_func) {...}

I bound this using Ninject:

Bind(typeof (IPriorityQueue<,>)).To(typeof(PriorityQueue<,>));

We had a weird bug in code and as part of this, we noticed that Ninject seems to generate an object Func and injects this into our priority queue, but we don't have a binding for this. The factory should have thrown an activtion exception, since we didn't pass the required comparison func. It didn't, and if I debug, I find this:

Ninject.dll!Ninject.KernelBase.Resolve.AnonymousMethod__c(Ninject.Planning.Bindings.IBinding binding) Line 386 + 0x5a bytes C#

How can I get Ninject to throw an exception as expected instead of silently generating a Func<> to inject?

Wilbert
  • 7,251
  • 6
  • 51
  • 91

1 Answers1

3

The Func-Auto binding to Func-Factory is a feature of Ninject.Extensions.Factory. See source: FuncModule.cs

If you create a specific binding for Func<T, T> it will still override the generic binding created by FuncModule. But as you noticed, there won't be any exceptions if you don't create that specific binding.

The simplest way to get rid of the default (open) binding is getting rid of the Factory extension.

But that is probably a bit drastic. What you can also do instead, is disabling Auto-Extension loading:

var kernel = new StandardKernel(new NinjectSettings {LoadExtensions = false});

Then you'll have to load extensions dynamically - this is done by loading their NinjectModule implementations. For example:

IKernel.Load<FuncModule>();

Of course the FuncModule you don't want to load, we're doing all this to get rid of it. But you'll have to do it for all the other extension modules you actually want. Finally you'll have to create all Factory extension bindings that you need:

if (!this.Kernel.GetBindings(typeof(Func<IContext, IResolutionRoot>)).Any())
{
    this.Bind<Func<IContext, IResolutionRoot>>().ToMethod(ctx => context => context.Kernel);
}

this.Bind<FuncProvider>().ToSelf().InSingletonScope();
this.Bind<IFunctionFactory>().To<FunctionFactory>();
this.Bind<IInstanceProvider>().To<StandardInstanceProvider>();

#if !SILVERLIGHT_20 && !WINDOWS_PHONE && !NETCF_35
this.Bind<IInterceptor>().To<FactoryInterceptor>()
    .When(request => typeof(IFactoryProxy).IsAssignableFrom(request.Target.Member.ReflectedType));
#endif

Note: If you are actually using the Func-Factories, i would recommend "ignoring" the issue. You now know where to look, should the issue ever arise again. If this is an isolated problem, you could also replace your Func<,> by an interface, making things more explicit (clean code). Of course this is not a perfect solution by any means. It's just one of the tough choices one has to make ;-)

BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • Do I understand this correctly: If I simply stop using the factory extension (= don't install the factory extension nupkg), the problem goes away? – Wilbert Jul 21 '14 at 11:26
  • But i also think the Factory Extensions provides some very valuable features. So I would either replace `Func<>` by specific interfaces or use only "half" of the extension by disabling auto-loading and creating a custom set of bindings for the Factory extension - as described in the answer. – BatteryBackupUnit Jul 21 '14 at 11:29
  • That's not going to work for me. The PriorityQueue comparison func is not intended to be bound, but specified in code (since we might have <, <=, >, >= comparisons for all objects). The bug in our code was that the factory method was Create() instead of Create(Func<...>), and it was not discovered because of the lacking exception throwing. I feel that this is a bug in the factory extensions, since a behavior that goes "will silently provide bindings for Func<>" is certainly not what the majority of users would expect. – Wilbert Jul 21 '14 at 11:36
  • I agree it would be better if it would be opt-in behavior (maybe separate nuget package Ninject.Extensions.FuncFactory) instead of opt-out. So yes this is far from perfect. Using only "half" (no `Func<>` binding) of the extensions would still work for you but involves some work (loading all extension modules manually). – BatteryBackupUnit Jul 21 '14 at 11:41
  • btw. AFAIK AutoFac and Unity behave the same in regards to `Func<>` and `Lazy<>` - they are "auto bound", too. – BatteryBackupUnit Jul 21 '14 at 11:42
  • I wrote a bug report on github: https://github.com/ninject/ninject.extensions.factory/issues/21, lets see what comes out of it. – Wilbert Jul 21 '14 at 11:51