WebAPI 2.1中的Ninject错误 – 确保控制器具有无参数的公共构造函数

我在WebAPI项目中安装了以下软件包及其依赖项:

Ninject.Web.WebApi Ninject.Web.WebApi.OwinHost

我这纯粹是作为web-api项目运行的。 没有MVC。

当我运行我的应用程序并向AccountController的Register操作发送POST时,我收到以下错误:

{ "message":"An error has occurred.", "exceptionMessage":"An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor.", "exceptionType":"System.InvalidOperationException", "stackTrace":" at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__0.MoveNext()", "innerException":{ "message":"An error has occurred.", "exceptionMessage":"Type 'RPT.Api.Controllers.AccountController' does not have a default constructor", "exceptionType":"System.ArgumentException", "stackTrace":" at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)" } } 

任何人都可以帮助我,因为我在Google上找到的唯一细节似乎是从2012年开始。

注意:我也尝试过AutoFac而不是Ninject,并在那里也遇到同样的错误。 最令人沮丧的。

这是我的NinjectWebCommon.cs:

 using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using RPT.Data; using RPT.Services; using RPT.Services.Interfaces; [assembly: WebActivatorEx.PreApplicationStartMethod(typeof(RPT.Api.NinjectWebCommon), "Start")] [assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(RPT.Api.NinjectWebCommon), "Stop")] namespace RPT.Api { 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(); ///  /// Starts the application ///  public static void Start() { DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule)); DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule)); bootstrapper.Initialize(CreateKernel); } ///  /// Stops the application. ///  public static void Stop() { bootstrapper.ShutDown(); } ///  /// Creates the kernel that will manage your application. ///  /// The created kernel. private static IKernel CreateKernel() { var kernel = new StandardKernel(); try { kernel.Bind<Func>().ToMethod(ctx => () => new Bootstrapper().Kernel); kernel.Bind().To(); RegisterServices(kernel); return kernel; } catch { kernel.Dispose(); throw; } } ///  /// Load your modules or register your services here! ///  /// The kernel. private static void RegisterServices(IKernel kernel) { kernel.Bind().ToSelf(); kernel.Bind<IUserStore>().To<UserStore>(); kernel.Bind<UserManager>().ToSelf(); kernel.Bind().To(); } } } 

这是我的AccountController:

 using System.Threading.Tasks; using System.Web.Http; using System.Web.ModelBinding; using Microsoft.AspNet.Identity; using RPT.Api.Models; using RPT.Services.Interfaces; namespace RPT.Api.Controllers { [RoutePrefix("api/account")] public class AccountController : ApiController { #region Initialisation private readonly IAccountService _accountService; public AccountController(IAccountService accountService) : base() { _accountService = accountService; } #endregion #region Actions [AllowAnonymous] [Route("register")] public async Task Register(UserRegistrationViewModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var result = await _accountService.RegisterUser(model.UserName, model.Password); var errorResult = GetErrorResult(result); return errorResult ?? Ok(); } #endregion #region Internal protected override void Dispose(bool disposing) { if (disposing) { _accountService.Dispose(); } base.Dispose(disposing); } private IHttpActionResult GetErrorResult(IdentityResult result) { if (result == null) { return InternalServerError(); } if (result.Succeeded) return null; if (result.Errors != null) { foreach (var error in result.Errors) { ModelState.AddModelError("", error); } } if (ModelState.IsValid) { // No ModelState errors are available to send, so just return an empty BadRequest. return BadRequest(); } return BadRequest(ModelState); } #endregion } } 

您是否修改了OWIN Startup类以调用app.UseNinjectWebApiapp.UseNinjectMiddleware而不是调用app.UseWebApi

Ninject Web API示例中的Startup.cs执行此操作…

在我的情况下,原因是解析器无法找到映射。 假设HomeController依赖于IDumb,解析器无法使用实现IDumb找到Dumb的具体实现。 换句话说,错误消息

 **No parameterless constructor defined for this object An error occurred when trying to create a controller of type 'ToDoListT1.WebApp.Controllers.HomeController'. Make sure that the controller has a parameterless public constructor** 

完全是误导。 在我的情况下,我刚刚通过添加对Dumb类的项目的引用来解决。 它应该是“没有找到IDumb的映射。”。 我不确定问题出在NInject或MS上。 什么花了我几个小时来找到这个。

我的解决方案是在构造函数中添加“public”关键字。

您缺少依赖项解析器,它是一个非常基本的实现:

 public class NinjectHttpDependencyResolver : IDependencyResolver, IDependencyScope { private readonly IKernel _kernel; public NinjectHttpDependencyResolver(IKernel kernel) { _kernel = kernel; } public IDependencyScope BeginScope() { return this; } public void Dispose() { //Do nothing } public object GetService(Type serviceType) { return _kernel.TryGet(serviceType); } public IEnumerable GetServices(Type serviceType) { return _kernel.GetAll(serviceType); } } 

然后在创建内核时注册它:

 var httpResolver = new NinjectHttpDependencyResolver(kernel); GlobalConfiguration.Configuration.DependencyResolver = httpResolver; 

确保已注册控制器使用的所有类型,一直到数据库。

在我的例子中,我只添加了控制器本身使用的接口,但没有添加用于实际查询数据库的接口。

请注意下面的代码中AddUserMaintenanceProcessor类如何具有控制器不知道的依赖项。 如果省略Unity(或您使用的任何IoC工具)键入这些依赖项的映射,则控制器构造将失败。

我的解决方案使用Unity,但我想说的是你需要为所有依赖项创建类型映射。

Startup.cs

 public void Configuration(IAppBuilder app) { var config = new HttpConfiguration(); // Configure Unity var resolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer()); GlobalConfiguration.Configuration.DependencyResolver = resolver; config.DependencyResolver = resolver; // Do Web API configuration WebApiConfig.Register(config); app.UseWebApi(config); } 

UnityConfig.cs

 public class UnityConfig { private static readonly Lazy Container = new Lazy(() => { var container = new UnityContainer(); RegisterTypes(container); return container; }); // Gets the configured Unity container public static IUnityContainer GetConfiguredContainer() { return Container.Value; } // Register type mappings public static void RegisterTypes(IUnityContainer container) { // LogManagerAdapter wrapping eg log4net container.RegisterType(); // AutoMapperAdapter wrapping eg AutoMapper (configuration omitted) container.RegisterType(); // Interface for persisting the user container.RegisterType(); // Interface for doing application logic in regards to adding a user container.RegisterType(); } } 

UsersController.cs

 public class UsersController : ApiController { private readonly IAddUserMaintenanceProcessor _addUserProcessor; public UsersV1Controller(IAddUserMaintenanceProcessor addUserProcessor) { _addUserProcessor = addUserProcessor; } public async Task Post(NewUser user) { return await _addUserProcessor.AddUserAsync(user); } // ... } 

AddUserMaintenanceProcessor.cs

 public class AddUserMaintenanceProcessor : IAddUserMaintenanceProcessor { private readonly IAddUserQueryProcessor _queryProcessor; private readonly ILog _logger; private readonly IAutoMapper _mapper; public AddUserMaintenanceProcessor( IAddUserQueryProcessor queryProcessor, ILogManager logManager, IAutoMapper mapper) { _queryProcessor = queryProcessor; _logger = logManager.GetLog(typeof(AddUserMaintenanceProcessor)); _mapper = mapper; } public async Task AddUserAsync(NewUser newUser) { _logger.Info($"Adding new user {newUser.UserName}"); // Map the NewUser object to a User object var user = _mapper.Map(newUser); // Persist the user to a medium unknown to this class, using the query processor, // which in turn returns a User object var addedUser = await _queryProcessor.AddUserAsync(user); // Map the User object back to UserModel to return to requester var userModel = _mapper.Map(addedUser); _logger.Info($"User {userModel.UserName} added successfully"); return userModel; } } 

我省略了处理器的接口,因为它们只包含一个方法(策略模式)。 用于记录和自动映射的接口与此问题无关。

AddUserQueryProcessor类只是将用户持久保存到数据库中。 再一次与这个问题无关。

对我来说,造成这个错误的原因是,对于我传递给构造函数的接口,我有一个命名绑定,我用错误的名称调用它。 正如您可以看到的那样,绑定表示它的名称为“Warehouse”,但我把事情搞混了,并将“DataWarehouse”放在构造函数中。 理解这一点会导致无参数构造函数的错误消失。

 private static void RegisterServices(IKernel kernel) { kernel.Bind().To().InThreadScope(); kernel.Bind().To().InThreadScope(); kernel.Bind().To().Named("Warehouse").WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["DataWarehouse"].ConnectionString); } 

构造函数:

 //WRONG NAME public VisitProcessor([Named("DataWarehouse")] IDbConnectionFactory connection) { _database = connection; }