0
votes

Having used ninject for a couple of years (at a basic level) I am struggling to get it work with webapi2 in a brand new solution. This worked fine for within an my mvc4 app with webapi.

error:

The usual suspect to tell you that binding isn't working

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
An error occurred when trying to create a controller of type 'TenantController'. Make sure that the controller has a parameterless public constructor.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'AIControlPoint.Data.Api.Controllers.TenantController' does not have a default constructor
</ExceptionMessage>
<ExceptionType>System.ArgumentException</ExceptionType>
<StackTrace>
at System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</InnerException>
</Error>

The webapi2 controller is fairly basic and does work if I create the context as part of a parameterless initialisation

    public class TenantController : ApiController
    {
        private TenantConfigurationContext tenantContext;

        public TenantController(TenantConfigurationContext tenantContext)
        {
            this.tenantContext = tenantContext;

        }

        public List<TenantConfiguration> Get()
        {
            var tenants = (from t in tenantContext.TenantConfigurations
                           select t)
                           .ToList();

            return tenants;
        }
    }
}

And my NinjectWebCommon is this

[assembly: WebActivator.PreApplicationStartMethod(typeof(AIControlPoint.Data.Api.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(AIControlPoint.Data.Api.App_Start.NinjectWebCommon), "Stop")]

namespace AIControlPoint.Data.Api.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;

    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>();

            System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Ninject.WebApi.DependencyResolver.NinjectDependencyResolver(kernel);

            RegisterServices(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<TenantConfigurationContext>().To<TenantConfigurationContext>();
        }        
    }
}

The nuget packages I've installed are: ninject, ninject.mvc3, ninject.web.common, ninject.webapi.dependencyresolver

Has anyone managed to get this working? any help would be much appreciated.

Thanks Steven

1

1 Answers

0
votes

You created an instance of TenantConfigurationContext directly inside the controller that will be strongly coupled TenantConfigurationContext. If any TenantConfigurationContext's implementation changes or new versions of that component are released, then you must change the controller class itself. Dependency injection gets rid of the tight coupling and makes the application flexible, reusable. You're using Ninject as the dependency framework, but the way you're trying to implement it has some problems. See below steps:

First you need to create an interface and class like below:

public interface ITenantConfigurationContext
{
    List<TenantConfiguration> TenantConfigurations();
}
public class TenantConfigurationContext : ITenantConfigurationContext
{
    public List<TenantConfiguration> TenantConfigurations()
    {
        return (get data from DB here);
    } 
}

Then invoke the ITenantConfigurationContext in Web Api controller:

public class TenantController : ApiController
{
    private ITenantConfigurationContext tenantContext;
    public TenantController(ITenantConfigurationContext tenantContext)
    {
        this.tenantContext = tenantContext;

    }
    public List<TenantConfiguration> Get()
    {
        var tenants = (from t in tenantContext.TenantConfigurations
                       select t)
                       .ToList();

        return tenants;
    }
}

Finally, register the ITenantConfigurationContext in Ninject:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ITenantConfigurationContext>().To<TenantConfigurationContext>();
} 

Note:

You may need to install Ninject.Web.WebApi-RC package from NuGet too.