Assicurati che il controller abbia un errore del costruttore pubblico senza parametri


105

Ho seguito questo tutorial che ha funzionato alla grande, fino a quando non ho modificato il mio DbContextper avere un costruttore aggiuntivo. Ora sto riscontrando problemi con la risoluzione e non sono sicuro di cosa fare per risolverlo. C'è un modo semplice per forzarlo ad afferrare il costruttore senza parametri o mi sto avvicinando a questo in modo errato?

DbContext con due costruttori:

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

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

SiteController costruttore:

private readonly IDashboardRepository _repo;

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

repository:

DashboardDbContext _context;

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

UnityResolver codice:

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

Errore dalla chiamata WebApi:

System.InvalidOperationException: si è verificato un errore durante il tentativo di creare un controller di tipo "SiteController". Assicurati che il controller abbia un costruttore pubblico senza parametri.

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: il tipo "Dashboard.Web.Controllers.SiteController" non dispone di un costruttore predefinito.

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)

Il tutorial è stato fantastico e ha funzionato bene per me fino a quando non ho aggiunto il secondo costruttore.


2
L'errore ti sta dicendo che SiteControllerè ciò che deve avere un costruttore senza parametri, non DashboardDbContext.
Neil Smith

Ciao Smith.h.Neil, ma genera quell'errore solo quando il costruttore aggiuntivo viene aggiunto al dbcontext. Se lo rimuovo o lo commento fuori (secondo costruttore) funziona bene.
scarpacci

Posso vedere il costruttore per SiteController?
Neil Smith

E immagino che tu stia iniettando il DbContextnel repository?
Neil Smith

@scarpacci Sei sicuro che l' unica modifica che stai apportando sia la rimozione del secondo costruttore da DbContext? A meno che non si ignori in qualche modo la creazione di istanze del controller non avendo il secondo costruttore DbContext, non avrebbe senso che l'errore dipendesse dai costruttori di DbContext.
Asad Saeeduddin

Risposte:


130

Quello che sta succedendo è che sei morso da questo problema . Fondamentalmente, quello che è successo è che non hai registrato i tuoi controller esplicitamente nel tuo contenitore. Unity tenta di risolvere i tipi concreti non registrati per te, ma poiché non può risolverli (a causa di un errore nella tua configurazione), restituisce null. È costretto a restituire null, perché l'API Web lo obbliga a farlo a causa del IDependencyResolvercontratto. Poiché Unity restituisce null, l'API Web tenterà di creare il controller stesso, ma poiché non dispone di un costruttore predefinito, verrà generata l'eccezione "Assicurarsi che il controller abbia un costruttore pubblico senza parametri". Questo messaggio di eccezione è fuorviante e non spiega la vera causa.

Avresti visto un messaggio di eccezione molto più chiaro se avessi registrato i tuoi controller in modo esplicito, ed è per questo che dovresti sempre registrare esplicitamente tutti i tipi di root.

Ma ovviamente, l'errore di configurazione deriva dall'aggiunta del secondo costruttore al tuo file DbContext. Unity cerca sempre di scegliere il costruttore con il maggior numero di argomenti, ma non ha idea di come risolvere questo particolare costruttore.

Quindi la vera causa è che stai cercando di utilizzare le funzionalità di cablaggio automatico di Unity per creare il file DbContext. DbContextè un tipo speciale che non dovrebbe essere cablato automaticamente. È un tipo di framework e dovresti quindi eseguire il fallback per registrarlo utilizzando un delegato di fabbrica :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 

NOTA BENE quando ricostruisci il tuo progetto puoi reimpostare le tue credenziali di accesso ... prima di provare ad applicare questa soluzione per favore: ricostruisci il tuo progetto, disconnettiti, quindi accedi di nuovo, solo allora - aggiorna la tua pagina e osserva se il problema persiste
ymz

Grazie - queste fragole nel mio team di backend infrangono molte regole con le configurazioni di unità. Cominciando a chiedermi se è così che ogni squadra utilizza i contenitori IOC.
Dagrooms

@Dagrooms: Molti sviluppatori sono un po 'per questo, ma questo non è un problema che esiste in tutti i contenitori DI. Il Simple Injector, ad esempio, garantirà sempre che venga richiesto un errore espressivo nel caso in cui ciò accada. Un altro buon suggerimento: non utilizzare un costume IDependencyResolverma usa solo un costume IControllerActivator.
Steven

46

Nel mio caso, è stato a causa di un'eccezione all'interno del costruttore della mia dipendenza iniettata (nel tuo esempio - all'interno del costruttore DashboardRepository). L'eccezione è stata rilevata da qualche parte all'interno dell'infrastruttura MVC. L'ho trovato dopo aver aggiunto i log in luoghi pertinenti.


7
Questa è una risposta davvero importante. È molto facile cadere nella trappola di inseguire i problemi di impostazione con Unity quando si vede, Make sure that the controller has a parameterless public constructor.ma è del tutto possibile che la dipendenza sia impostata ma un'eccezione nelle viscere ha impedito che venga risolta.
Phil Cooper

2
Questo. Un milione di volte! Ho dimenticato di aggiungere una mappa delle dipendenze alla mia configurazione Ninject.
Travo

La mia eccezione nel profondo delle viscere era un tipo di proprietà di "stringa" quando avrebbe dovuto essere "DateTime?". Non l'avrei cercato se non avessi visto questa risposta. Grazie mille
Jazzy

Più simile a LousyErrorMessageException ()
Simon_Weaver

In quali luoghi rilevanti hai posizionato il registro? Sono stato eseguito dal debugger ma non ho ottenuto eccezioni, anche quando ho controllato tutte le eccezioni CLR. Ho dovuto aggiungere la risoluzione manuale del costruttore e solo allora ho ricevuto l'errore. Mi ha detto di aggiungere Diagnostic per ottenere un errore utilizzabile, che finalmente mi ha dato qualcosa su cui lavorare
Arjan

6

Ho avuto lo stesso problema e l'ho risolto apportando modifiche al file UnityConfig.cs Per risolvere il problema di dipendenza nel file UnityConfig.cs devi aggiungere:

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

4

A volte, poiché stai risolvendo la tua interfaccia in ContainerBootstraper.cs, è molto difficile rilevare l'errore. Nel mio caso si è verificato un errore nel risolvere l'implementazione dell'interfaccia che ho inserito nel controller API. Non sono riuscito a trovare l'errore perché ho risolto l'interfaccia nel mio bootstraperContainer in questo modo: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
quindi ho aggiunto la seguente riga nel mio bootstrap container: container.RegisterType<MyController>(); quindi quando compilo il progetto, il compilatore si è lamentato e si è fermato nella riga sopra e ha mostrato l'errore .


4

Ho avuto lo stesso problema. L'ho cercato su Google per due giorni. Finalmente ho notato per caso che il problema era il modificatore di accesso del costruttore del Controller. Non ho messo la publicparola chiave dietro il costruttore del controller.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Aggiungo questa esperienza come un'altra risposta forse qualcun altro ha commesso un errore simile.


0

Se hai un'interfaccia nel tuo controller

public myController(IXInterface Xinstance){}

È necessario registrarli nel contenitore Dependency Injection.

container.Bind<IXInterface>().To<XClass>().InRequestScope();

0

Ho ricevuto questo errore quando ho definito accidentalmente una proprietà come un tipo di oggetto specifico, invece del tipo di interfaccia che ho definito in UnityContainer.

Per esempio:

Definizione di UnityContainer:

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

SiteController (nel modo sbagliato - avviso tipo repo):

private readonly DashboardRepository _repo;

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

SiteController (nel modo giusto):

private readonly IDashboardRepository _repo;

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

0

Se stai usando UnityConfig.cs per resister alle mappature del tuo tipo come sotto.

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

Devi far sapere **webApiConfig.cs**a Container

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);

0

Nel mio caso, Unity si è rivelato una falsa pista. Il mio problema era il risultato di diversi progetti destinati a diverse versioni di .NET. Unity è stato impostato correttamente e tutto è stato registrato correttamente nel contenitore. Tutto è stato compilato bene. Ma il tipo era in una libreria di classi e la libreria di classi era impostata per targetizzare .NET Framework 4.0. Il progetto WebApi che utilizza Unity è stato impostato per targetizzare .NET Framework 4.5. La modifica della libreria di classi in modo che anche come destinazione 4.5 abbia risolto il problema per me.

L'ho scoperto commentando il costruttore DI e aggiungendo il costruttore predefinito. Ho commentato i metodi del controller e li ho fatti lanciare NotImplementedException. Ho confermato che potevo raggiungere il controller e vedendo la mia NotImplementedException mi ha detto che stava creando un'istanza del controller correttamente. Successivamente, nel costruttore predefinito, ho istanziato manualmente la catena di dipendenze invece di fare affidamento su Unity. È ancora compilato, ma quando l'ho eseguito il messaggio di errore è tornato. Questo mi ha confermato che ho ancora ricevuto l'errore anche quando Unity era fuori dai giochi. Alla fine, ho iniziato dalla parte inferiore della catena e sono salito, commentando una riga alla volta e ripetendo il test finché non ho più ricevuto il messaggio di errore. Questo mi ha indirizzato nella direzione della classe incriminata, e da lì ho capito che era isolata da una singola assemblea.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.