Upewnij się, że kontroler ma błąd konstruktora publicznego bez parametrów

105

Postępowałem zgodnie z tym samouczkiem, który działał świetnie, dopóki nie zmodyfikowałem mojego, DbContextaby mieć dodatkowego konstruktora. Mam teraz problemy z rozdzielczością i nie wiem, co zrobić, aby to naprawić. Czy istnieje łatwy sposób, aby zmusić go do pobrania konstruktora bez parametrów, czy podchodzę do tego niepoprawnie?

DbContext z dwoma konstruktorami:

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

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

SiteController konstruktor:

private readonly IDashboardRepository _repo;

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

Magazyn:

DashboardDbContext _context;

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

UnityResolver kod:

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

Błąd wywołania WebApi:

System.InvalidOperationException: Wystąpił błąd podczas próby utworzenia kontrolera typu „SiteController”. Upewnij się, że kontroler ma konstruktora publicznego bez parametrów.

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” nie ma domyślnego konstruktora.

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)

Samouczek był świetny i działał dobrze, dopóki nie dodałem drugiego konstruktora.

scarpacci
źródło
2
Błąd mówi ci, że SiteControllerto musi mieć konstruktor bez parametrów, a nie DashboardDbContext.
Neil Smith
Cześć Smith.h.Neil, ale zgłasza ten błąd tylko wtedy, gdy dodatkowy konstruktor jest dodawany do kontekstu dbcontext. Jeśli to usunę lub skomentuję (drugi konstruktor), działa dobrze.
scarpacci
Czy mogę zobaczyć konstruktora SiteController?
Neil Smith
I zgaduję, że wstrzykujesz to DbContextdo repozytorium?
Neil Smith
@scarpacci Czy na pewno jedyną wprowadzaną zmianą jest usunięcie drugiego konstruktora z DbContext? O ile w jakiś sposób nie pominiesz tworzenia wystąpienia kontrolera, nie mając drugiego konstruktora DbContext, nie miałoby sensu uzależnienie błędu od konstruktorów DbContext.
Asad Saeeduddin

Odpowiedzi:

130

Dzieje się tak, że ten problem cię ugryzł . Zasadniczo zdarzyło się, że nie zarejestrowałeś kontrolerów jawnie w swoim kontenerze. Unity próbuje rozwiązać za Ciebie niezarejestrowane konkretne typy, ale ponieważ nie może ich rozwiązać (z powodu błędu w konfiguracji), zwraca wartość null. Jest zmuszony zwrócić wartość null, ponieważ interfejs API sieci Web wymusza to na podstawie IDependencyResolverkontraktu. Ponieważ Unity zwraca wartość null, interfejs API sieci Web spróbuje samodzielnie utworzyć kontroler, ale ponieważ nie ma domyślnego konstruktora, zgłosi wyjątek „Upewnij się, że kontroler ma publiczny konstruktor bez parametrów”. Ten komunikat o wyjątku jest mylący i nie wyjaśnia prawdziwej przyczyny.

Widziałbyś znacznie wyraźniejszy komunikat o wyjątku, gdybyś jawnie zarejestrował swoje kontrolery i dlatego zawsze powinieneś jawnie rejestrować wszystkie typy rootów.

Ale oczywiście błąd konfiguracji pochodzi z dodania drugiego konstruktora do pliku DbContext. Unity zawsze próbuje wybrać konstruktora z największą liczbą argumentów, ale nie ma pojęcia, jak rozwiązać ten konkretny konstruktor.

Więc prawdziwą przyczyną jest to, że próbujesz użyć możliwości automatycznego łączenia Unity, aby utworzyć plik DbContext. DbContextto specjalny typ, którego nie należy łączyć automatycznie. Jest to typ frameworku i dlatego należy cofnąć się do zarejestrowania go przy użyciu delegata fabryki :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
źródło
PROSZĘ PAMIĘTAĆ , że podczas przebudowy projektu możesz zresetować dane logowania ... przed próbą zastosowania tego rozwiązania: odbuduj projekt, wyloguj się, a następnie zaloguj się ponownie, dopiero potem - odśwież stronę i obserwuj, czy problem nie ustąpi
ymz
Dziękuję - te dingleberries w moim zespole zaplecza łamią wiele zasad w konfiguracjach Unity. Zaczynam się zastanawiać, czy w ten sposób każdy zespół używa kontenerów IOC.
Dagrooms
@Dagrooms: Wielu programistów jest tego zdania, ale nie jest to problem występujący we wszystkich DI Containers. Na przykład Simple Injector zawsze zapewni wyraźny błąd w przypadku wystąpienia takiej sytuacji. Kolejna dobra wskazówka: nie używaj niestandardowego, IDependencyResolvera IControllerActivatorzamiast niego użyj niestandardowego .
Steven
46

W moim przypadku było to spowodowane wyjątkiem wewnątrz konstruktora mojej wstrzykniętej zależności (w twoim przykładzie - wewnątrz konstruktora DashboardRepository). Wyjątek został przechwycony gdzieś w infrastrukturze MVC. Znalazłem to po dodaniu logów w odpowiednich miejscach.

Illidan
źródło
7
To naprawdę ważna odpowiedź. Bardzo łatwo jest wpaść w pułapkę ścigania problemów z konfiguracją w Unity, gdy widzisz, Make sure that the controller has a parameterless public constructor.ale jest całkiem możliwe, że zależność jest skonfigurowana, ale wyjątek głęboko w trzewiach uniemożliwił jej rozwiązanie.
Phil Cooper
2
To. Milion razy! Zapomniałem dodać mapę zależności do mojej konfiguracji Ninject.
Travo
Moim głębokim wyjątkiem był typ właściwości „string”, podczas gdy powinno to być „DateTime?”. Nie szukałbym tego, gdybym nie widział tej odpowiedzi. Bardzo dziękuję.
Jazzy
Bardziej jak LousyErrorMessageException ()
Simon_Weaver
W jakich odpowiednich miejscach umieściłeś dziennik? Uruchomiłem z debuggera, ale nie dostałem żadnego wyjątku, nawet gdy sprawdziłem wszystkie wyjątki CLR. Musiałem dodać ręczne rozwiązanie konstruktora i dopiero wtedy pojawił się błąd. Powiedział mi, żebym dodał Diagnostic, aby uzyskać użyteczny błąd, który w końcu dał mi coś do pracy
Arjan
6

Miałem ten sam problem i rozwiązałem go, wprowadzając zmiany w pliku UnityConfig.cs.Aby rozwiązać problem z zależnościami w pliku UnityConfig.cs, musisz dodać:

public static void RegisterComponents()    
{
    var container = new UnityContainer();
    container.RegisterType<ITestService, TestService>();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
befree2j
źródło
4

Czasami, ponieważ rozwiązujesz swój interfejs w ContainerBootstraper.cs, bardzo trudno jest wychwycić błąd. W moim przypadku wystąpił błąd w rozwiązaniu implementacji interfejsu, który wstrzyknąłem do kontrolera API. Nie mogłem znaleźć błędu, ponieważ rozwiązałem interfejs w moim bootstraperContainerze w następujący sposób: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
następnie dodałem następujący wiersz w moim kontenerze bootstrap: container.RegisterType<MyController>(); więc kiedy kompiluję projekt, kompilator narzekał i zatrzymał się w powyższej linii i pokazał błąd .

Amir978
źródło
4

Miałem ten sam problem. Wyszukiwałem go w Google przez dwa dni. W końcu przypadkowo zauważyłem, że problemem był modyfikator dostępu konstruktora kontrolera. Nie umieściłem publicsłowa kluczowego za konstruktorem kontrolera.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

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

Dodam to doświadczenie jako kolejną odpowiedź, być może ktoś inny popełnił podobny błąd.

Bobs
źródło
0

Jeśli masz interfejs w kontrolerze

public myController(IXInterface Xinstance){}

Musisz zarejestrować je w kontenerze Dependency Injection.

container.Bind<IXInterface>().To<XClass>().InRequestScope();
Ahmet Arslan
źródło
0

Wystąpił ten błąd, gdy przypadkowo zdefiniowałem właściwość jako określony typ obiektu, zamiast typu interfejsu, który zdefiniowałem w UnityContainer.

Na przykład:

Definiowanie UnityContainer:

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

SiteController (zły sposób - typ repozytorium powiadomień):

private readonly DashboardRepository _repo;

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

SiteController (właściwy sposób):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Arcydzieło
źródło
0

Jeśli używasz UnityConfig.cs do ochrony mapowań twojego typu, jak poniżej.

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

Musisz poinformować **webApiConfig.cs**o pojemniku

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);
Vinay Patel
źródło
0

W moim przypadku Unity okazała się czerwonym śledziem. Mój problem był wynikiem różnych projektów dotyczących różnych wersji .NET. Unity został poprawnie skonfigurowany i wszystko zostało poprawnie zarejestrowane w kontenerze. Wszystko dobrze skompilowane. Ale typ znajdował się w bibliotece klas, a biblioteka klas była ustawiona na platformę .NET Framework 4.0. Projekt WebApi korzystający z Unity został ustawiony jako docelowy .NET Framework 4.5. Zmiana biblioteki klas na docelową 4.5 rozwiązała problem.

Odkryłem to, wykomentowując konstruktor DI i dodając domyślny konstruktor. Zakomentowałem metody kontrolera i kazałem im wyrzucić NotImplementedException. Potwierdziłem, że mogę skontaktować się z kontrolerem, a widząc mój wyjątek NotImplementedException powiedział mi, że instancja kontrolera działa poprawnie. Następnie w domyślnym konstruktorze ręcznie utworzyłem wystąpienie łańcucha zależności zamiast polegać na Unity. Wciąż się skompilował, ale kiedy go uruchomiłem, wrócił komunikat o błędzie. Potwierdziło to dla mnie, że nadal otrzymuję błąd, nawet gdy Unity był poza obrazem. W końcu zacząłem od dołu łańcucha i wspinałem się w górę, komentując jedną linię na raz i ponownie testując, aż nie otrzymałem już komunikatu o błędzie. To skierowało mnie w stronę przestępczej klasy i stamtąd doszedłem do wniosku, że została ona odizolowana do jednego zgromadzenia.

Charlie Kilian
źródło