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

时间:2014-06-12 22:57:05

标签: c# asp.net-web-api dependency-injection ninject asp.net-web-api2

我在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.<SendAsync>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();

        /// <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();
            try
            {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

                RegisterServices(kernel);


                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<RptContext>().ToSelf();

            kernel.Bind<IUserStore<IdentityUser>>().To<UserStore<IdentityUser>>();
            kernel.Bind<UserManager<IdentityUser>>().ToSelf();

            kernel.Bind<IAccountService>().To<AccountService>();
        }
    }
}

这是我的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<IHttpActionResult> 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
    }
}

10 个答案:

答案 0 :(得分:19)

您是否修改了OWIN Startup课程以致电app.UseNinjectWebApiapp.UseNinjectMiddleware而不是致电app.UseWebApi

Ninject Web API示例中的

Startup.cs执行此操作...

答案 1 :(得分:13)

在我的情况下,原因是解析器无法找到映射。假设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类的项目的引用来解决。应该是&#34;没有找到IDumb的映射。&#34;。我不确定问题出在NInject或MS上。我花了几个小时才找到它。

答案 2 :(得分:3)

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

答案 3 :(得分:3)

对我有用的是按如下说明添加NuGet软件包Ninject.Web.WebApi.WebHosthttps://github.com/ninject/Ninject.Web.WebApi/wiki/Setting-up-an-mvc-webapi-application

答案 4 :(得分:0)

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

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<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType);
    }
}

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

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

答案 5 :(得分:0)

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

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

请注意下面的代码中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<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
    {
        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 e.g. log4net
        container.RegisterType<ILogManager, LogManagerAdapter>();

        // AutoMapperAdapter wrapping e.g. AutoMapper (configuration omitted)
        container.RegisterType<IAutoMapper, AutoMapperAdapter>();

        // Interface for persisting the user
        container.RegisterType<IAddUserQueryProcessor, AddUserQueryProcessor>();

        // Interface for doing application logic in regards to adding a user
        container.RegisterType<IAddUserMaintenanceProcessor, AddUserMaintenanceProcessor>();
    }
}

<强> UsersController.cs

public class UsersController : ApiController
{
    private readonly IAddUserMaintenanceProcessor _addUserProcessor;

    public UsersV1Controller(IAddUserMaintenanceProcessor addUserProcessor)
    {
        _addUserProcessor = addUserProcessor;
    }

    public async Task<UserModel> 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<UserModel> AddUserAsync(NewUser newUser)
    {
        _logger.Info($"Adding new user {newUser.UserName}");

        // Map the NewUser object to a User object
        var user = _mapper.Map<User>(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<UserModel>(addedUser);

        _logger.Info($"User {userModel.UserName} added successfully");

        return userModel;
    }
}

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

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

答案 6 :(得分:0)

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

private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IVisitProcessor>().To<VisitProcessor>().InThreadScope();
        kernel.Bind<IPatientProcessor>().To<PatientProcessor>().InThreadScope();
        kernel.Bind<IDbConnectionFactory>().To<SqlConnectionFactory>().Named("Warehouse").WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["DataWarehouse"].ConnectionString);
    }

构造

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

答案 7 :(得分:0)

我也试图制作一个“仅API”应用,但遇到了类似的问题。对我个人而言,最后要选择正确的起始模板。

尽管只使用API​​,但是在“创建新的ASP.NET Web应用程序”向导中选择“空”应用程序类型,并在“添加文件夹和核心引用”部分中选择MVC和Web API Ninject工作正常:

enter image description here

与“ Web API”应用程序相对,该应用程序没有与Ninject配合得很好:

enter image description here

此后,只需添加Ninject.Web.WebApi.WebHost程序包,该程序包将添加NinjectWebCommon.cs类,并开始添加依赖项。

enter image description here

答案 8 :(得分:0)

我有一个需要服务的情况,并向其中注入了其他服务。这些注入服务中的一个具有另一个注入服务,其中一个未正确注册以进行依赖项注入。

我在注入控制器的服务的构造函数中设置了一个断点(导致问题的服务之前的服务)。在调试中,显示了它无法解决依赖关系的服务。

答案 9 :(得分:0)

正如其他答案所指出的那样,错误消息可能会引起误解。 Ninject可能工作正常,但可能由于多种原因而无法实例化某些依赖项,例如:

  • 缺少对间接依赖项的绑定:https://stackoverflow.com/a/36438210/2279059
  • 依赖项没有默认的构造函数,并且不支持依赖项注入(例如,缺少import Board from './Board'; jest.mock('./Board', () => { const BoardSpy = jest.requireActual('./Board').default; return { default: jest.fn((...args) => new BoardSpy(...args)), __esModule: true, }; }); it('instantiates a Board', () => { const board = new Board(); expect(Board).toBeCalled(); }); 属性)。
  • 两个服务之间的周期性依赖关系

您看不到服务器返回的错误中的所有详细信息(可能是设计使然)。如果连接调试器,则除非在调试设置中禁用“ Just My Code” ,否则可能也无法捕获该异常。然后,您可能可以捕获内部Ninject异常,该异常可能包含了解问题所需的所有信息。

较旧版本的Ninject,或者如果您未使用[Inject],还需要按照https://stackoverflow.com/a/24196092/2279059中的说明,将Ninject注册为依赖项解析器。

相关问题