web-dev-qa-db-fra.com

Le contrôleur Api Web MVC 4 ne possède pas de constructeur par défaut?

Voici la trace:

<Error>
   <Message>An error has occurred.</Message>

   <ExceptionMessage>
      Type 'ProjectName.Web.Api.Controllers.ContinentsController' does not have a default constructor
   </ExceptionMessage>

   <ExceptionType>System.ArgumentException</ExceptionType>

   <StackTrace>
      at System.Linq.Expressions.Expression.New(Type type)

      at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)

      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>
</Error>

Je trouve cela bizarre car public class UsersController : ApiController { ... } fonctionne très bien. J'ai comparé les 2 contrôleurs, tous les paramètres et structures sont similaires.

J'utilise Ninject et la configuration de mon système est similaire à Jamie KurtzAsp.Net Mvc 4 et Web Api: création d'un service REST de bout en bout }.

À partir de la trace de la pile, est-ce que quelqu'un peut repérer le problème et comment le résoudre? Merci!

Tel que demandé.

ContinentsController 

[LoggingNHibernateSession]
public class ContinentsController : ApiController
{
    private readonly ISession _session;
    private readonly IContinentMapper _continentMapper;
    private readonly IHttpContinentFetcher _httpContinentFetcher;
    private readonly IDateTime _dateTime;

    public ContinentsController(ISession session, IContinentMapper continentMapper, IHttpContinentFetcher continentFetcher, IDateTime dateTime)
    {
        _session = session;
        _continentMapper = continentMapper;
        _httpContinentFetcher = continentFetcher;
        _dateTime = dateTime;
    }

    public IEnumerable<Continent> Get()
    {
        var continents = _session
            .Query<Data.Model.Continent>()
            .Select(_continentMapper.CreateContinent)
            .ToList();

        return continents;
    }

    public Continent Get(long id)
    {
        var modelContinent = _httpContinentFetcher.GetContinent(id);
        var continent = _continentMapper.CreateContinent(modelContinent);

            return continent;
        }
  }

UsersController: Fonctionne très bien.

    public class UsersController : ApiController
    {
        private readonly ISession _session;
        private readonly IUserManager _userManager;
        private readonly IUserMapper _userMapper;
        private readonly IHttpUserFetcher _userFetcher;

        public UsersController(
            IUserManager userManager,
            IUserMapper userMapper,
            IHttpUserFetcher userFetcher,
            ISession session)
        {
            _userManager = userManager;
            _userMapper = userMapper;
            _userFetcher = userFetcher;
            _session = session;
        }

        [Queryable]
        public IQueryable<Data.Model.User> Get()
        {
            return _session.Query<Data.Model.User>();
        }

        [LoggingNHibernateSession]
        public User Get(Guid id)
        {
            var user = _userFetcher.GetUser(id);
            return _userMapper.CreateUser(user);
        }
    }

J'utilise NinjectWebCommon.cs et dans celui-ci, j'ai ceci et quelques autres méthodes par défaut .:

        private static void RegisterServices(IKernel kernel)
        {
            var containerConfigurator = new NinjectConfigurator();
            containerConfigurator.Configure(kernel);

            GlobalConfiguration.Configuration.MessageHandlers.Add(kernel.Get<BasicAuthenticationMessageHandler>());
        }

Alors j'ai NinjectConfigurator.cs:

public class NinjectConfigurator
{
    ......
    private void AddBindings(IKernel container)
        {
            .....
            container.Bind<IDateTime>().To<DateTimeAdapter>();
            container.Bind<IDatabaseValueParser>().To<DatabaseValueParser>();

            //HttpFetchers
            container.Bind<IHttpUserFetcher>().To<HttpUserFetcher>();
            container.Bind<IHttpContinentFetcher>().To<HttpContinentFetcher>();                                

            //TypeMappers
            container.Bind<IUserManager>().To<UserManager>();
            container.Bind<IMembershipInfoProvider>().To<MembershipAdapter>();
            container.Bind<IUserMapper>().To<UserMapper>();
            container.Bind<IContinentMapper>().To<ContinentMapper>();    
            .........
        }
     .......
}

NinjectWebCommon.cs et NinjectConfigurator.cs se trouvent tous deux dans le dossier App_Start

container.Bind<ISession>().ToMethod(CreateSession); est NHibernate. C'est dans NinjectConfigurator.cs dans private void ConfigureNHibernate(IKernel container) { ... }

16
Komengem

Cette erreur est une erreur connue lorsque vous ne définissez pas le résolveur de dépendances dans votre application. La Controller Factory n'a pas pu trouver de constructeur sans paramètre pour créer le contrôleur et exécuter la méthode d'action (ou la méthode verb??). Vous devez donc créer une classe de résolveur de dépendances et la définir lors de l'initialisation de votre application Web. Cela résoudra les dépendances de vos contrôleurs.

En utilisant ninject, vous pouvez essayer quelque chose comme ceci:

using Ninject; 
using Ninject.Syntax; 
using System; 
using System.Collections.Generic; 
using System.Diagnostics.Contracts; 
using System.Web.Http.Dependencies; 

namespace MyApplication.App_Start 
{     
    public class NinjectDependencyScope : IDependencyScope     
    {         
        private IResolutionRoot resolver;         

        internal NinjectDependencyScope(IResolutionRoot resolver)         
        {             
            Contract.Assert(resolver != null);             
            this.resolver = resolver;         
        }         

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

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

            return resolver.TryGet(serviceType);         
        }   

        public IEnumerable GetServices(Type serviceType)         
        {             
            if (resolver == null)                 
                throw new ObjectDisposedException("this", "This scope has already been disposed");             

            return resolver.GetAll(serviceType);         
        }     
    }     

    public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver     
    {         
        private IKernel kernel;         
        public NinjectDependencyResolver(IKernel kernel)             
            : base(kernel)         
        {             
            this.kernel = kernel;         
        }         

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

La classe NinjectDependencyResolver prend un objet Ninject StandardKernel comme argument de constructeur et cette référence est utilisée chaque fois qu'une portée de dépendance est mise en pipeline . Pour que tout cela fonctionne, la classe NinjectDependencyResolver est affectée à la configuration globale de l'application:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
    kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

    // register all your dependencies on the kernel container
    RegisterServices(kernel);

    // register the dependency resolver passing the kernel container
    GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);

    return kernel;
}

Dans votre fichier Global.asax.cs à la fin de l'événement Application_Start, appelez cette méthode CreateKernel.

16
Felipe Oriani

Vous devez indiquer à Ninject comment résoudre correctement les dépendances de l'API Web. 

Vous pouvez utiliser la réponse de Felipe Oriani, mais si vous le souhaitez, il existe un package NuGet appelé WebApiContrib.IoC.Ninject qui le fera pour vous.

  1. Dans Visual Studio, accédez à: Outils> Gestionnaire de paquets NuGet> Gérer Packages NuGet pour solution

  2. Installez le WebApiContrib.IoC.Ninject package

  3. Edit: NinjectWebCommon.cs et mettez à jour la méthode CreateKernel () pour inclure: GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver (kernel);

    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);
    
            //Note: Add the line below:
            GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
    
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }
    
16

J'ai également rencontré cette erreur identique mais provenant d'une autre cause en utilisant Ninject. En fait, le résolveur de dépendance et la configuration étaient corrects. En fait, l'application fonctionnait bien jusqu'à ce que j'essaye de résoudre une autre nouvelle dépendance. J'avais l'enregistrement prévu pour cela, donc je ne pouvais pas comprendre ce qui manquait.

Le problème était que j'ai accidentellement résolu l'interface avec le même interface, par opposition à la résolution avec le type de béton approprié. Donc, en utilisant un exemple de l’OP, j’ai fait ce qui suit: c’est la même erreur:

container.Bind<IUserManager>().To<IUserManager>();

Avis résolvant en IUserManager qui était un oubli, mais ne conduit à la même erreur de la part du PO. Évidemment, la solution consiste à résoudre le type de béton approprié.

2
atconway

La réponse acceptée est celle de 2014. Étant donné que de nombreuses personnes ont eu ce problème, à partir de 2016, il existe une recette pour cela. Ceci ne fait qu'ajouter à la réponse acceptée.

Je le fais en installant les paquets Ninject.MVC3 et WebApiContrib.IoC.Ninject. Un fichier appelé NinjectWebCommon est ajouté à votre dossier App_Start. Vous pouvez ensuite utiliser la variable NinjectResolver intégrée.

Ajoutez simplement cette méthode:

private static void AddWebApiSupport(StandardKernel kernel)
{
  // Support WebAPI
  GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
  GlobalConfiguration.Configuration.Services.Add(typeof(IFilterProvider), new NinjectWebApiFilterProvider(kernel));
}

et appelez-le avant d'appeler RegisterServices.

1
andrei.ciprian

Pour moi, il suffisait d'ajouter le paquet Ninject.Web.WebApi.Webhost NuGet.

Pour mes applications utilisant à la fois MVC et WebApi2, j'ai les packages suivants pour Ninject:

  • Ninject
  • Ninject.MVC5
  • Ninject.Web.Common
  • Ninject.Web.Common.WebHost
  • Ninject.Web.WebApi
  • Ninject.Web.WebApi.WebHost
1
Jamie M
private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<ICountingKsRepository>().To<CountingKsRepository>();

Assurez-vous que la partie To <> est une classe concrète et non une interface, car cela créera également la même erreur!

0
Andrew Day

J'ai eu ce problème aussi, mais il s'est avéré qu'une de mes interfaces n'était implémentée par aucune classe (j'ai oublié de l'ajouter à la déclaration de classe).

0
Mark Cidade

Il s'agit d'un problème courant qui se produit principalement lorsque vous travaillez avec Ninject Dependency injection Container . Veuillez suivre les étapes pour résoudre ce problème .  Make sure you have Installed the following packages.

Encore une fois, optez pour ceci et installez-le-  enter image description here

Maintenant, vérifiez le dossier App_start.Vous trouverez la chose suivante (NinjectWebcommon.cs) .  enter image description here

Maintenant, double-cliquez et vérifiez la ligne suivante dans la méthode CreateKernel () comme ceci

 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);
            GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
            return kernel;
        }
        catch
        {
            kernel.Dispose();
            throw;
        }
    }

Et puis enregistrez votre dépendance comme ça

 private static void RegisterServices(IKernel kernel)
    {

        kernel.Bind<IProductDetails>().To<ProductDetails>().InRequestScope();
    }      

Je suis sûr que cela fera fonctionner votre solution.

0
Debendra Dash