Assurez-vous que le contrôleur a une erreur de constructeur public sans paramètre

105

J'ai suivi ce tutoriel qui a très bien fonctionné, jusqu'à ce que je modifie mon DbContextpour avoir un constructeur supplémentaire. J'ai maintenant des problèmes avec la résolution et je ne sais pas quoi faire pour résoudre ce problème. Existe-t-il un moyen simple de le forcer à saisir le constructeur sans paramètre ou j'aborde cela de manière incorrecte?

DbContext avec deux constructeurs:

public class DashboardDbContext : DbContext
{
    public DashboardDbContext() : base("DefaultConnection") { }

    public DashboardDbContext(DbConnection dbConnection, bool owns)
        : base(dbConnection, owns) { }
}

SiteController constructeur:

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}

Dépôt:

DashboardDbContext _context;

public DashboardRepository(DashboardDbContext context)
{
    _context = context;
}

UnityResolver code:

public class UnityResolver : IDependencyResolver
{
    private readonly IUnityContainer _container;

    public UnityResolver(IUnityContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return _container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = _container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

WebApiConfig:

var container = new UnityContainer();
container.RegisterType<IDashboardRepository, DashboardRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);

Erreur de l'appel WebApi:

System.InvalidOperationException: une erreur s'est produite lors de la tentative de création d'un contrôleur de type «SiteController». Assurez-vous que le contrôleur a un constructeur public sans paramètre.

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

InnerException: System.ArgumentException: le type «Dashboard.Web.Controllers.SiteController» n'a pas de constructeur par défaut.

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)

Le didacticiel était génial et a bien fonctionné pour moi jusqu'à ce que j'ajoute le deuxième constructeur.

Scarpacci
la source
2
L'erreur vous dit que SiteControllerc'est ce qui doit avoir un constructeur sans paramètre, non DashboardDbContext.
Neil Smith
Bonjour Smith.h.Neil, mais cela ne génère cette erreur que lorsque le constructeur supplémentaire est ajouté au dbcontext. Si je le supprime ou le commente (deuxième constructeur), cela fonctionne bien.
scarpacci
Puis-je voir le constructeur pour SiteController?
Neil Smith
Et je suppose que vous injectez le DbContextdans le référentiel?
Neil Smith
@scarpacci Êtes-vous sûr que le seul changement que vous apportez est la suppression du deuxième constructeur du DbContext? À moins que vous ne contourniez d'une manière ou d'une autre l'instanciation de votre contrôleur en ne disposant pas du deuxième constructeur DbContext, cela n'aurait aucun sens que l'erreur dépende des constructeurs de DbContext.
Asad Saeeduddin

Réponses:

130

Ce qui se passe, c'est que vous êtes mordu par ce problème . Fondamentalement, ce qui s'est passé, c'est que vous n'avez pas enregistré vos contrôleurs explicitement dans votre conteneur. Unity essaie de résoudre les types concrets non enregistrés pour vous, mais comme il ne peut pas le résoudre (causé par une erreur dans votre configuration), il renvoie null. Il est forcé de renvoyer null, car l'API Web l'oblige à le faire en raison du IDependencyResolvercontrat. Puisque Unity renvoie null, l'API Web essaiera de créer le contrôleur lui-même, mais comme elle n'a pas de constructeur par défaut, elle lèvera l'exception «Assurez-vous que le contrôleur a un constructeur public sans paramètre». Ce message d'exception est trompeur et n'explique pas la vraie cause.

Vous auriez vu un message d'exception beaucoup plus clair si vous aviez enregistré vos contrôleurs explicitement, et c'est pourquoi vous devriez toujours enregistrer tous les types de racine explicitement.

Mais bien sûr, l'erreur de configuration vient de l'ajout du deuxième constructeur à votre DbContext. Unity essaie toujours de choisir le constructeur avec le plus d'arguments, mais il n'a aucune idée de comment résoudre ce constructeur particulier.

La vraie cause est donc que vous essayez d'utiliser les capacités de câblage automatique d'Unity pour créer le fichier DbContext. DbContextest un type spécial qui ne devrait pas être câblé automatiquement. C'est un type de framework et vous devriez donc revenir à son enregistrement à l'aide d'un délégué d'usine :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
la source
VEUILLEZ NOTER que lorsque vous reconstruisez votre projet, vous pouvez réinitialiser vos identifiants de connexion ... avant d'essayer d'appliquer cette solution, veuillez: reconstruire votre projet, vous déconnecter, puis vous reconnecter, alors seulement - actualisez votre page et observez si le problème persiste
ymz
Merci - ces dingleberries de mon équipe backend enfreignent beaucoup de règles avec les configurations d'unité. Commencer à se demander si c'est ainsi que chaque équipe utilise les conteneurs IOC.
Dagrooms
@Dagrooms: De nombreux développeurs sont peu enclins à cela, mais ce n'est pas un problème qui existe dans tous les conteneurs DI. Simple Injector, par exemple, s'assurera toujours qu'une erreur expressive est invoquée au cas où une telle chose se produirait. Un autre bon conseil: n'utilisez pas de coutume IDependencyResolvermais utilisez uniquement une coutume à la IControllerActivatorplace.
Steven
46

Dans mon cas, c'était à cause d'une exception à l'intérieur du constructeur de ma dépendance injectée (dans votre exemple - à l'intérieur du constructeur DashboardRepository). L'exception a été interceptée quelque part dans l'infrastructure MVC. J'ai trouvé cela après avoir ajouté des journaux aux endroits pertinents.

Illidan
la source
7
C'est une réponse vraiment importante. Il est très facile de tomber dans le piège de la poursuite des problèmes de configuration avec Unity en voyant, Make sure that the controller has a parameterless public constructor.mais il est fort possible que la dépendance soit configurée mais une exception profondément dans les entrailles a empêché sa résolution.
Phil Cooper
2
Ce. Un million de fois! J'ai oublié d'ajouter une carte de dépendance à ma configuration Ninject.
Travo
Mon exception profonde dans les entrailles était un type de propriété de «chaîne» alors qu'il aurait dû être «DateTime?». Je n'aurais pas cherché cela si je n'avais pas vu cette réponse. Merci beaucoup.
Jazzy
Plus comme LousyErrorMessageException ()
Simon_Weaver
À quels endroits pertinents avez-vous placé le journal? J'ai couru à partir du débogueur mais je n'ai eu aucune exception, même lorsque j'ai vérifié toutes les exceptions CLR. J'ai dû ajouter une résolution manuelle du constructeur et ce n'est qu'alors que j'ai eu l'erreur. Il m'a dit d'ajouter Diagnostic pour obtenir une erreur utilisable, cela m'a finalement donné quelque chose avec quoi travailler
Arjan
6

J'ai eu le même problème et je l'ai résolu en apportant des modifications dans le fichier UnityConfig.cs Afin de résoudre le problème de dépendance dans le fichier UnityConfig.cs, vous devez ajouter:

public static void RegisterComponents()    
{
    var container = new UnityContainer();
    container.RegisterType<ITestService, TestService>();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
befree2j
la source
4

Parfois, parce que vous résolvez votre interface dans ContainerBootstraper.cs, il est très difficile d'attraper l'erreur. Dans mon cas, une erreur s'est produite lors de la résolution de l'implémentation de l'interface que j'ai injectée dans le contrôleur api. Je n'ai pas pu trouver l'erreur car j'ai résolu l'interface dans mon bootstraperContainer comme ceci: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
alors j'ai ajouté la ligne suivante dans mon conteneur d'amorçage: container.RegisterType<MyController>(); donc quand je compile le projet, le compilateur s'est plaint et s'est arrêté dans la ligne ci-dessus et a montré l'erreur .

Amir978
la source
4

J'ai eu le même problème. Je l'ai googlé pendant deux jours. Enfin, j'ai accidentellement remarqué que le problème était le modificateur d'accès du constructeur du contrôleur. Je n'ai pas mis le publicmot clé derrière le constructeur du contrôleur.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

        public MyController(IMyClass myClass)
        {
            _myClass = myClass;
        }
    }

J'ajoute cette expérience comme une autre réponse, peut-être que quelqu'un d'autre a fait une erreur similaire.

Bobs
la source
0

Si vous avez une interface dans votre contrôleur

public myController(IXInterface Xinstance){}

Vous devez les enregistrer dans le conteneur d'injection de dépendances.

container.Bind<IXInterface>().To<XClass>().InRequestScope();
Ahmet Arslan
la source
0

J'ai cette erreur lorsque j'ai accidentellement défini une propriété comme un type d'objet spécifique, au lieu du type d'interface que j'ai défini dans UnityContainer.

Par exemple:

Définition de UnityContainer:

var container = new UnityContainer();
container.RegisterInstance(typeof(IDashboardRepository), DashboardRepository);
config.DependencyResolver = new UnityResolver(container);

SiteController (dans le mauvais sens - notez le type de dépôt):

private readonly DashboardRepository _repo;

public SiteController(DashboardRepository repo)
{
    _repo = repo;
}

SiteController (la bonne façon):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Chef-d'oeuvre
la source
0

Si vous utilisez UnityConfig.cs pour résister aux mappages de votre type comme ci-dessous.

public static void RegisterTypes(IUnityContainer container)
    {
     container.RegisterType<IProductRepository, ProductRepository>();
    }

Vous devez informer **webApiConfig.cs**Container

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);
Vinay Patel
la source
0

Dans mon cas, Unity s'est avéré être un hareng rouge. Mon problème était le résultat de différents projets ciblant différentes versions de .NET. Unity a été configuré correctement et tout a été correctement enregistré avec le conteneur. Tout s'est bien compilé. Mais le type se trouvait dans une bibliothèque de classes et la bibliothèque de classes a été définie pour cibler .NET Framework 4.0. Le projet WebApi utilisant Unity a été défini pour cibler .NET Framework 4.5. Changer la bibliothèque de classes pour cibler également 4.5 a résolu le problème pour moi.

J'ai découvert cela en commentant le constructeur DI et en ajoutant un constructeur par défaut. J'ai commenté les méthodes du contrôleur et leur ai demandé de lancer NotImplementedException. J'ai confirmé que je pouvais atteindre le contrôleur, et voir mon NotImplementedException m'a dit qu'il instanciait correctement le contrôleur. Ensuite, dans le constructeur par défaut, j'ai instancié manuellement la chaîne de dépendances au lieu de compter sur Unity. Il était toujours compilé, mais quand je l'ai exécuté, le message d'erreur est revenu. Cela m'a confirmé que j'avais toujours l'erreur même lorsque Unity était hors de l'image. Enfin, j'ai commencé au bas de la chaîne et j'ai progressé en commentant une ligne à la fois et en retestant jusqu'à ce que je ne reçoive plus le message d'erreur. Cela m'a orienté dans la direction de la classe incriminée, et à partir de là, j'ai compris qu'elle était isolée à une seule assemblée.

Charlie Kilian
la source