Ninject throws Activation Exception in a WebApi project with multiple assemblies
Asked Answered
B

3

5

My asp.net WebApi project comprises of multiple assemblies for Services, Core and Data Access. In an attempt to use Ninject as my DI container in the project, I added Ninject.Web.Common package from NuGet. Then, I Implemented IDependencyResolver as:

public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
    readonly IKernel kernel;

    public NinjectDependencyResolver(IKernel kernel) : base(kernel)
    {
        this.kernel = kernel;
    }

    public IDependencyScope BeginScope()
    {
        return new NinjectDependencyScope(this.kernel.BeginBlock());
    }
}

public class NinjectDependencyScope : IDependencyScope
{
    IResolutionRoot resolver;

    public NinjectDependencyScope(IResolutionRoot resolver)
    {
        this.resolver = resolver;
    }

    public object GetService(System.Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");

        var resolved = this.resolver.Get(serviceType);
        return resolved;
    }

    public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType)
    {
        if (resolver == null)
            throw new ObjectDisposedException("this", "This scope has been disposed");

        return this.resolver.GetAll(serviceType);
    }

    public void Dispose()
    {
        IDisposable disposable = resolver as IDisposable;
        if (disposable != null)
            disposable.Dispose();

        resolver = null;
    }
}

Here is my Ninject.Web.Common.cs.

public static class NinjectWebCommon 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
        RegisterServices(kernel);

        GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind(x =>
            x.FromAssembliesInPath(AppDomain.CurrentDomain.RelativeSearchPath)
            .SelectAllIncludingAbstractClasses()
            .BindDefaultInterface()
            .Configure(config => config.InSingletonScope()));

        //kernel.Bind(x => 
        //    {
        //        x.FromAssembliesMatching("*")
        //        .SelectAllClasses()
        //        .BindDefaultInterface()
        //        .Configure(b => b.InTransientScope());
        //    });
        //kernel.Load()
        //kernel.Bind<ISecurityService>().To<SecurityServiceImplementation>();

        //kernel.Bind(x => x
        //    .FromAssembliesMatching("*")
        //    .SelectAllClasses()
        //    .BindDefaultInterface());
        //.Configure(b => b.InTransientScope()));
        //kernel.Load("*.dll");
    }        
}

exception is

[ActivationException: Error activating IHostBufferPolicySelector
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for IHostBufferPolicySelector

I have used various registrations (commented out) but none work. The break point in NinjectWebCommon.cs -> CreateKernel() method is hit and so does the break point in GetService(System.Type serviceType) method. AppDomain.CurrentDomain.RelativeSearchPath resolves to the bin directory of the app and it contains all the dlls including System.Web.Http.dll which contains the IHostBufferPolicySelector type.

How can I properly use the Ninject.Extensions.Conventions to setup the kernel for type resolution?

Burget answered 12/11, 2012 at 16:12 Comment(0)
B
6

From the hints in the answer by Remo and comment by Filip along with a significant amount of debugging time, I found out the use of this.resolver.Get(serviceType) instead of this.resolver.TryGet(serviceType) in GetService() implementation was the culprit in my situation.

I plan a detailed blog post about this but the short of it is that once we have the NinjectDependencyResolver plugged into MVC using the line: GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel); and we don't define Framework level dependency bindings (e.g. IHostBufferPolicySelector etc.), an exception is raised by the Get() method for some framework level dependencies when they are not resolved through Ninject. Using TryGet() does not raise an exception and the framework falls back to default dependencies for unresolved (a.ka. null) dependencies like IHostBufferPolicySelector. So, the options are

  1. Use the TryGet() method to resolve dependencies.
  2. Wrap Get in Try/Catch and discard the exception.
Burget answered 14/11, 2012 at 17:17 Comment(0)
H
2

Try this post. Instead of catching the Ninject exception, catch the exception for all WebApi calls. http://blog.greatrexpectations.com/2013/05/15/exception-handling-for-web-api-controller-constructors/ In the stacktrace, the constructor where the exception occured would be visible.

Haematoma answered 9/8, 2013 at 14:15 Comment(0)
R
1

There is no class HostBufferPolicySelector so there in no class for which IHostBufferPolicySelector is the default interface. You may try BindAllInterfaces or BindDefaultInterfaces.

Recalescence answered 12/11, 2012 at 16:30 Comment(6)
Remo, thanks for your suggestion. When I change BindDefaultInterface to BindDefaultInterfaces, I experience: An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll Any ideas?Burget
Actually you should define your conventions more precisely. Such conventions like all classes from all assemblies inclusing all System assemblies is very unpredictable. Think about what conventions you have for your classes and specify them with the convention syntax. Classes where no convention applies can still be bound on their own with single service binding syntax.Recalescence
Remo, I can do that for my own assemblies and shall. However, I am not sure about how would I specify framework level (asp.net, mvc, webapi) bindings like IHostBufferPolicySelector? Is there a way to auto bind System/Framework bindings?Burget
The default implementation for IHostBufferPolicySelector is WebHostBufferPolicySelector (not HostBufferPolicySelector) that's why Ninject can't find it on its own. However, as Remo said - why would you want to bind the system assemblies? Web API can take care of its own dependencies and there is no reason for you to meddle with them so I would definitely avoid the generic "bind all classes from all assemblies".Survival
Filip, thank you for looking into this. I must be missing something really obvious here but when I replace the default dependency resolver using: GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel); How would WebApi resolve it's dependencies w/o any bindings being specified?Burget
Also, I Modified the binding spec to this kernel.Bind(x => x.FromAssembliesMatching("WebApiTest.*.dll") .SelectAllClasses() .BindAllInterfaces() .Configure(config => config.InTransientScope())); and it throws the exception for IHostBufferPolicySelector. My intention is to simply inject my dependencies not the framework dependencies. Thanks for your help.Burget

© 2022 - 2024 — McMap. All rights reserved.