11

I'm using Ninject.Extensions.Factory with Ninject 3 to create a factory, which creates different types of IFoo based on the string provided to the factory. I've got a passing unit test, but oddly, only in the Resharper test runner. In the NCrunch test runner it fails. Is this a NCrunch config issue, or do I need to change the code?

The interface:

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

The Ninject bindings:

kernel.Bind<IFooFactory>().ToFactory(() => new UseFirstParameterAsNameInstanceProvider());
kernel.Bind<IFoo>().To<BarFoo>().Named("Bar");

The test:

[Test]
public void CanCreateFooTest()
{
    var factory = (IFooFactory) Kernel.GetService(typeof(IFooFactory));
    var bar = factory.CreateFoo("Bar");
    Assert.AreEqual(typeof(BarFoo), bar.GetType());
}

And the NCrunch exception:

System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
  ----> Ninject.ActivationException : Error activating IInterceptor using conditional implicit self-binding of IInterceptor
Provider returned null.
Activation path:
  2) Injection of dependency IInterceptor into parameter  of constructor of type IFooFactoryProxy
  1) Request for IFooFactory

Suggestions:
  1) Ensure that the provider handles creation requests properly.

   at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Ninject.Infrastructure.Language.ExtensionsForIEnumerable.ToArraySlow(IEnumerable series, Type elementType) in c:\Projects\Ninject\ninject\src\Ninject\Infrastructure\Language\ExtensionsForIEnumerable.cs:line 29
   at Ninject.Planning.Targets.Target`1.ResolveWithin(IContext parent) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Targets\Target.cs:line 149
   at Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:line 114
   at Ninject.Activation.Providers.StandardProvider.<>c__DisplayClass4.<Create>b__2(ITarget target) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:line 96
   at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Ninject.Activation.Providers.StandardProvider.Create(IContext context) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:line 95
   at Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\ninject\src\Ninject\Activation\Context.cs:line 157
   at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 386
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
   at Ninject.KernelBase.System.IServiceProvider.GetService(Type service) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 553
   at FooProject.Tests.CanCreateFooTest() in C:\Projects\FooProject ...
--ActivationException
   at Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\ninject\src\Ninject\Activation\Context.cs:line 165
   at Ninject.KernelBase.<>c__DisplayClass10.<Resolve>b__c(IBinding binding) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:line 386
   at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
   at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
harriyott
  • 10,505
  • 10
  • 64
  • 103

4 Answers4

10

Go into the NCrunch configuration for the unit test library and set Copy referenced assemblies to workspace to True.

NCrunch confuguration screenshot

shamp00
  • 11,106
  • 4
  • 38
  • 81
7

Here the code that works with NCrunch:

        var kernel = new StandardKernel();
        kernel.Bind<IFooFactory>().ToFactory(() => new UseFirstParameterAsNameInstanceProvider());
        kernel.Bind<IFoo>().To<BarFoo>().Named("Bar");
        kernel.Load<FuncModule>();

        var factory = kernel.Get<IFooFactory>();
        var bar = factory.CreateFoo("Bar");
        Assert.Equal(typeof(BarFoo), bar.GetType());

UPDATE

This works great, and sorts out NCrunch. However, Resharper complains that it has been loaded twice. The workaround:

    #if NCRUNCH
        Kernel.Load<FuncModule>(); 
    #endif
harriyott
  • 10,505
  • 10
  • 64
  • 103
the_joric
  • 11,986
  • 6
  • 36
  • 57
  • 3
    I won't claim it's better, but until NInject offers something like EnsureLoaded, an alternative to the #if (if you want a different approach) is checking to see if it's already been loaded via Kernel.HasModule(typeof(FuncModule).FullName) – James Manning May 20 '12 at 23:13
  • We hit the same issue in a project where we dynamically extract all the assemblies (including ninject) from embedded resources in the main exe. This solution works here too (manually loading `FuncModule`). In our case, we fixed it with @JamesManning's solution as it's an already built dll used in several places. – James Thorpe Apr 28 '15 at 14:45
5

The FuncModule is not loaded when running with that TestRunner. This happens in case the extension is not copied to the startup directory of the executed process.

I don't NCrunch. So I can't tell you what it is doing. But most likely it copies the assemblies in a different way than the R# test runner. You could load the extensions manually but this feels like a hack.

Remo Gloor
  • 32,665
  • 4
  • 68
  • 98
2

I have been using harriyott's suggestion for a year or so. But then this problem happend on our TFS-Buildserver, too. So now I avoid Ninject automatically loading of all Extensions and Load them manually. This avoids the #if, #endif and the same code will run on Resharper and NCrunch:

var kernel = new StandardKernel(new NinjectSettings { LoadExtensions = false});
kernel.Load<FuncModule>();

the rest is unchanged:

kernel.Bind<IFooFactory>().ToFactory(() => new UseFirstParameterAsNameInstanceProvider());
kernel.Bind<IFoo>().To<BarFoo>().Named("Bar");

var factory = kernel.Get<IFooFactory>();
var bar = factory.CreateFoo("Bar");
Assert.Equal(typeof(BarFoo), bar.GetType());