it-swarm.com.de

Ninject-Fehler in WebAPI 2.1 - Stellen Sie sicher, dass der Controller über einen öffentlichen Parameter ohne Parameter verfügt

Ich habe die folgenden Pakete und ihre Abhängigkeiten in meinem WebAPI-Projekt installiert:

Ninject.Web.WebApiNinject.Web.WebApi.OwinHost

Ich betreibe dies rein als Web-API-Projekt. Kein MVC.

Wenn ich meine Anwendung starte und eine POST an die Register-Aktion von AccountController sende, erhalte ich die folgende Fehlermeldung:

{
"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)"
}
}

Kann mir jemand helfen, denn die einzigen Details, die ich bei Google finden kann, scheinen aus dem Jahr 2012 zu stammen.

Hinweis: Ich habe auch AutoFac anstelle von Ninject ausprobiert und dort auch den gleichen Fehler erhalten. Sehr frustrierend

Hier ist mein 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>();
        }
    }
}

Hier ist mein 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
    }
}
17
Hades

Haben Sie Ihre OWIN-Klasse Startup so geändert, dass app.UseNinjectWebApi und app.UseNinjectMiddleware aufgerufen wird, anstatt app.UseWebApi aufzurufen?

Startup.cs tut dies in den Ninject Web API-Beispielen ...

19
Dean Ward

In meinem Fall konnte Resolver keine Zuordnung finden. Angenommen, HomeController hat eine Abhängigkeit von IDumb. Der Resolver konnte keine konkrete Implementierung von Dumb mit den IDumb-Implementierungen finden. .__ Mit anderen Worten: die Fehlermeldung 

**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**

ist völlig irreführend. In meinem Fall habe ich nur einen Verweis auf das Projekt der Klasse Dumb hinzugefügt. Es sollte so etwas wie "Es konnte keine Zuordnung für IDumb gefunden werden" sein. Ich bin nicht sicher, ob das Problem bei NInject oder MS liegt. Was auch immer ich brauchte Stunden, um das herauszufinden.

12
VivekDev

Meine Lösung ist das Hinzufügen eines öffentlichen Schlüsselworts zum Konstruktor.

3

Der Grund für diesen Fehler war für mich, dass für die Schnittstelle, die ich in den Konstruktor übergeben hatte, ich eine benannte Bindung hatte und sie mit dem falschen Namen bezeichnete. Wie Sie unten sehen können, heißt es in der Bindung "Warehouse", aber ich habe die Dinge durcheinander gebracht und "DataWarehouse" in den Konstruktor eingefügt. Durch die Behebung dieses Problems wurde der Fehler in einem parameterlosen Konstruktor behoben.

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

konstrukteur:

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

Stellen Sie sicher, dass Sie alle vom Controller verwendeten Typen bis hin zur Datenbank registriert haben.

In meinem Fall hatte ich nur die Schnittstelle hinzugefügt, die der Controller selbst verwendet hat, nicht jedoch die Schnittstelle, über die die Datenbank tatsächlich abgefragt wurde.

Beachten Sie im folgenden Code, wie die AddUserMaintenanceProcessor-Klasse Abhängigkeiten hat, von denen der Controller nichts weiß. Wenn Sie die Typzuordnungen für diese Abhängigkeiten von Unity (oder dem von Ihnen verwendeten IoC-Tool) weglassen, schlägt die Controller-Konstruktion fehl.

Meine Lösung verwendet Unity. Ich möchte jedoch darauf hinweisen, dass Sie Typzuordnungen für alle Abhängigkeiten erstellen müssen.

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;
    }
}

Ich habe die Schnittstellen für die Prozessoren weggelassen, da sie nur eine Methode enthalten (Strategiemuster). Die Schnittstellen für die Protokollierung und das automatische Mapping sind für diese Frage irrelevant.

Die Klasse AddUserQueryProcessor hält den Benutzer lediglich für die Datenbank. Wieder einmal irrelevant für diese Frage.

0

Ihnen fehlt ein Abhängigkeitsauflöser, es ist eine wirklich grundlegende Implementierung:

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

Dann registrieren Sie es einfach, wenn Sie den Kernel erstellen:

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