Stellen Sie sicher, dass der Controller einen parameterlosen öffentlichen Konstruktorfehler aufweist

105

Ich habe dieses Tutorial befolgt, das großartig funktioniert hat, bis ich mein geändert habe DbContext, um einen zusätzlichen Konstruktor zu haben. Ich habe jetzt Probleme mit der Lösung und bin mir nicht sicher, was ich tun soll, um dies zu beheben. Gibt es eine einfache Möglichkeit, es zu zwingen, den parameterlosen Konstruktor zu greifen, oder gehe ich dies falsch an?

DbContext mit zwei Konstruktoren:

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

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

SiteController Konstrukteur:

private readonly IDashboardRepository _repo;

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

Repository:

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

Fehler beim WebApi-Aufruf:

System.InvalidOperationException: Beim Versuch, einen Controller vom Typ 'SiteController' zu erstellen, ist ein Fehler aufgetreten. Stellen Sie sicher, dass der Controller über einen parameterlosen öffentlichen Konstruktor verfügt.

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: Typ 'Dashboard.Web.Controllers.SiteController' hat keinen Standardkonstruktor.

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)

Das Tutorial war großartig und hat für mich gut funktioniert, bis ich den zweiten Konstruktor hinzugefügt habe.

Scarpacci
quelle
2
Der Fehler sagt Ihnen, dass SiteControllerdies ein paramertloser Konstruktor sein muss, nicht DashboardDbContext.
Neil Smith
Hallo Smith.h.Neil, aber dieser Fehler wird nur ausgelöst, wenn der zusätzliche Konstruktor zum Datenbankkontext hinzugefügt wird. Wenn ich das entferne oder auskommentiere (zweiter Konstruktor), funktioniert es gut.
Scarpacci
Kann ich den Konstruktor für sehen SiteController?
Neil Smith
Und ich vermute, Sie injizieren das DbContextin das Repository?
Neil Smith
@scarpacci Sind Sie sicher, dass Sie nur den zweiten Konstruktor aus dem DbContext entfernen? Wenn Sie die Instanziierung Ihres Controllers nicht irgendwie umgehen, indem Sie nicht über den zweiten DbContext-Konstruktor verfügen, ist es nicht sinnvoll, dass der Fehler von den Konstruktoren des DbContext abhängt.
Asad Saeeduddin

Antworten:

130

Was passiert ist, dass Sie von diesem Problem gebissen werden . Grundsätzlich haben Sie Ihre Controller nicht explizit in Ihrem Container registriert. Unity versucht, nicht registrierte konkrete Typen für Sie aufzulösen. Da es jedoch nicht aufgelöst werden kann (verursacht durch einen Fehler in Ihrer Konfiguration), wird null zurückgegeben. Es ist gezwungen, null zurückzugeben, da die Web-API dies aufgrund des IDependencyResolverVertrags dazu zwingt . Da Unity null zurückgibt, versucht die Web-API, den Controller selbst zu erstellen. Da sie jedoch keinen Standardkonstruktor hat, wird die Ausnahme "Stellen Sie sicher, dass der Controller einen parameterlosen öffentlichen Konstruktor hat" ausgelöst. Diese Ausnahmemeldung ist irreführend und erklärt nicht die wahre Ursache.

Sie hätten eine viel klarere Ausnahmemeldung gesehen, wenn Sie Ihre Controller explizit registriert hätten. Deshalb sollten Sie immer alle Root-Typen explizit registrieren.

Aber natürlich kommt der Konfigurationsfehler von Ihnen, wenn Sie den zweiten Konstruktor zu Ihrem hinzufügen DbContext. Unity versucht immer, den Konstruktor mit den meisten Argumenten auszuwählen, hat jedoch keine Ahnung, wie dieser bestimmte Konstruktor aufgelöst werden soll.

Die eigentliche Ursache ist also, dass Sie versuchen, die automatischen Verkabelungsfunktionen von Unity zu verwenden, um das zu erstellen DbContext. DbContextist ein spezieller Typ, der nicht automatisch verkabelt werden sollte. Es handelt sich um einen Framework-Typ, und Sie sollten daher auf die Registrierung mithilfe eines Factory-Delegaten zurückgreifen :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
quelle
BITTE BEACHTEN SIE, wenn Sie Ihr Projekt neu erstellen, können Sie Ihre Anmeldeinformationen zurücksetzen. Bevor Sie versuchen, diese Lösung anzuwenden, erstellen Sie bitte Folgendes: Erstellen Sie Ihr Projekt neu, melden Sie sich ab und erst wieder an - aktualisieren Sie Ihre Seite und beobachten Sie, ob das Problem weiterhin besteht
ymz
Vielen Dank - diese Dingleberries in meinem Backend-Team brechen mit den Unity-Konfigurationen viele Regeln. Ich frage mich, ob jedes Team so IOC-Container verwendet.
Dagrooms
@Dagrooms: Viele Entwickler sind davon betroffen, aber dies ist kein Problem, das in allen DI-Containern vorhanden ist. Simple Injector zum Beispiel stellt immer sicher, dass ein Ausdrucksfehler ausgelöst wird, falls so etwas passiert. Ein weiterer guter Tipp: Verwenden Sie keine benutzerdefinierte, IDependencyResolversondern nur eine benutzerdefinierte IControllerActivator.
Steven
46

In meinem Fall lag dies an einer Ausnahme innerhalb des Konstruktors meiner injizierten Abhängigkeit (in Ihrem Beispiel - innerhalb des DashboardRepository-Konstruktors). Die Ausnahme wurde irgendwo in der MVC-Infrastruktur festgestellt. Ich habe dies gefunden, nachdem ich Protokolle an relevanten Stellen hinzugefügt habe.

Illidan
quelle
7
Dies ist eine wirklich wichtige Antwort. Es ist sehr leicht, in die Falle zu tappen, wenn man Unity-Probleme beim Einrichten verfolgt, Make sure that the controller has a parameterless public constructor.aber es ist durchaus möglich, dass die Abhängigkeit eingerichtet ist, aber eine Ausnahme tief im Darm hat die Lösung verhindert.
Phil Cooper
2
Dies. Eine Million Mal! Ich habe vergessen, meiner Ninject-Konfiguration eine Abhängigkeitskarte hinzuzufügen.
Travo
Meine tief im Darm liegende Ausnahme war ein Eigenschaftstyp von 'string', wenn es 'DateTime?' Hätte sein sollen. Hätte ich nicht danach gesucht, hätte ich diese Antwort nicht gesehen. Vielen Dank.
Jazzy
Eher wie LousyErrorMessageException ()
Simon_Weaver
An welchen relevanten Stellen haben Sie das Protokoll platziert? Ich lief vom Debugger aus, bekam aber keine Ausnahme, selbst wenn ich alle CLR-Ausnahmen überprüfte. Ich musste eine manuelle Konstruktorauflösung hinzufügen und erst dann bekam ich den Fehler. Es sagte mir, ich solle Diagnostic hinzufügen, um einen brauchbaren Fehler zu erhalten, der mir endlich etwas gab, mit dem ich arbeiten konnte
Arjan
6

Ich hatte das gleiche Problem und habe es behoben, indem ich Änderungen an der Datei UnityConfig.cs vorgenommen habe. Um das Abhängigkeitsproblem in der Datei UnityConfig.cs zu beheben, müssen Sie Folgendes hinzufügen:

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

Manchmal ist es sehr schwierig, den Fehler zu erkennen, da Sie Ihre Schnittstelle in ContainerBootstraper.cs auflösen. In meinem Fall ist ein Fehler beim Auflösen der Implementierung der Schnittstelle aufgetreten, die ich dem API-Controller injiziert habe. Ich konnte den Fehler nicht finden, weil ich die Schnittstelle in meinem bootstraperContainer container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
folgendermaßen aufgelöst habe : Dann habe ich die folgende Zeile in meinen Bootstrap-Container eingefügt: container.RegisterType<MyController>(); Als ich das Projekt kompilierte, beschwerte sich der Compiler und stoppte in der obigen Zeile und zeigte den Fehler an .

Amir978
quelle
4

Ich hatte das gleiche Problem. Ich habe es zwei Tage lang gegoogelt. Endlich habe ich versehentlich bemerkt, dass das Problem der Zugriffsmodifikator des Konstruktors des Controllers war. Ich habe das publicSchlüsselwort nicht hinter den Konstruktor des Controllers gestellt.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Ich füge diese Erfahrung als eine andere Antwort hinzu, vielleicht hat jemand anderes einen ähnlichen Fehler gemacht.

Bobs
quelle
0

Wenn Sie eine Schnittstelle in Ihrem Controller haben

public myController(IXInterface Xinstance){}

Sie müssen sie im Dependency Injection-Container registrieren.

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

Ich habe diesen Fehler erhalten, als ich versehentlich eine Eigenschaft als einen bestimmten Objekttyp anstelle des in UnityContainer definierten Schnittstellentyps definiert habe.

Beispielsweise:

UnityContainer definieren:

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

SiteController (der falsche Weg - Repo-Typ beachten):

private readonly DashboardRepository _repo;

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

SiteController (der richtige Weg):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Meisterstück
quelle
0

Wenn Sie UnityConfig.cs verwenden, um die Zuordnungen Ihres Typs wie unten beschrieben zu speichern .

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

Sie müssen das **webApiConfig.cs**über Container informieren

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

In meinem Fall stellte sich Unity als roter Hering heraus. Mein Problem war das Ergebnis verschiedener Projekte, die auf verschiedene Versionen von .NET abzielten. Unity wurde richtig eingerichtet und alles wurde korrekt mit dem Container registriert. Alles gut zusammengestellt. Der Typ befand sich jedoch in einer Klassenbibliothek, und die Klassenbibliothek wurde auf .NET Framework 4.0 ausgerichtet. Das WebApi-Projekt mit Unity wurde als Ziel für .NET Framework 4.5 festgelegt. Das Ändern der Klassenbibliothek auf Ziel 4.5 hat das Problem für mich behoben.

Ich habe dies entdeckt, indem ich den DI-Konstruktor auskommentiert und einen Standardkonstruktor hinzugefügt habe. Ich habe die Controller-Methoden auskommentiert und sie NotImplementedException auslösen lassen. Ich bestätigte, dass ich den Controller erreichen konnte, und als ich meine NotImplementedException sah, wurde mir mitgeteilt, dass der Controller in Ordnung ist. Als Nächstes habe ich im Standardkonstruktor die Abhängigkeitskette manuell instanziiert, anstatt mich auf Unity zu verlassen. Es wurde immer noch kompiliert, aber als ich es ausführte, kam die Fehlermeldung zurück. Dies bestätigte für mich, dass ich den Fehler auch dann noch bekam, wenn Unity nicht im Bilde war. Schließlich begann ich am Ende der Kette und arbeitete mich nach oben, kommentierte jeweils eine Zeile aus und testete erneut, bis ich die Fehlermeldung nicht mehr erhielt. Dies wies mich in die Richtung der beleidigenden Klasse und von dort stellte ich fest, dass es zu einer einzigen Versammlung isoliert war.

Charlie Kilian
quelle