Casablanca: Kein parameterloser Konstruktor

Hallo,

ich versuche momentan mein Programm umzustellen und dieses via Dependency Injection zu aktualisieren. Das Problem dabei ist, dass ich eine bis dahin nicht vorhandene Fehlermeldung

Für dieses Objekt wurde kein parameterloser Konstruktor definiert.

bekomme, wenn ich versuche, einen Parameter-Behafteten Konstruktor zu definieren, in dem mein von Unity übergebenes Objekt zugewiesen werden soll. Hat da jemand eine Idee, wie man dieses Problem umgehen kann?

        private readonly IMyContext _myContext;

        public MyController(IMyContext myContext)
        {
            _myContext = myContext;
        }

Gruß

  1. Tach!

    ich versuche momentan mein Programm umzustellen und dieses via Dependency Injection zu aktualisieren. Das Problem dabei ist, dass ich eine bis dahin nicht vorhandene Fehlermeldung

    Für dieses Objekt wurde kein parameterloser Konstruktor definiert.

    bekomme, wenn ich versuche, einen Parameter-Behafteten Konstruktor zu definieren, in dem mein von Unity übergebenes Objekt zugewiesen werden soll. Hat da jemand eine Idee, wie man dieses Problem umgehen kann?

    Die naheliegendste Lösung wäre, einen parameterlosen Konstruktor hinzuzufügen. Aber das wäre nur die halbe Wahrheit, denn dann fehlt dir ja das Objekt, wenn dieser Konstruktor verwendet wird. Die zielführende Vorgehensweise wäre also, dafür zu sorgen, dass der parameterlose Konstruktor nicht verwendet wird.

    Wenn DI nicht vorgesehen ist, dann steckt da Code im Framework, der einen parameterlosen Konstruktor aufruft. Und du musst nun dafür sorgen, dass die Instantiierungsroutine gegen eine ausgetauscht wird, die mit DI umgehen kann, und das passende Objekt erzeugt oder findet, das da übergeben werden muss. Entsprechenden Code muss das DI-Framework mitbringen und auch eine Anleitung, wie man das korrekt für die jeweilige Umgebung (bei dir ASP.NET MVC) initialisiert.

    dedlfix.

    1. Guten Tag,

      danke. DI via Unity habe ich bereits mehrmals in den anderen Projekten eingesetzt und bis jetzt habe ich auch keine Probleme diesbezüglich gehabt. Unity hat immer brav funktioniert und nie wurde nach einem parameterlosen Konstruktor verlangt. Dass auf einmal so was vorkommt, überrascht mich.

      Überraschender ist, dass DI in einem Controller einwandfrei funktioniert, in einem anderen aber nicht. Sogar habe ich Probleme in einem Konsolen-Anwendung. Im Internet stehen auch Lösungen, die in meinem Fall überhaupt nicht funktionieren.

      Gruß

      1. Tach!

        DI via Unity habe ich bereits mehrmals in den anderen Projekten eingesetzt und bis jetzt habe ich auch keine Probleme diesbezüglich gehabt. Unity hat immer brav funktioniert und nie wurde nach einem parameterlosen Konstruktor verlangt. Dass auf einmal so was vorkommt, überrascht mich.

        Ich kann dir auch nicht sagen, woran das liegt, ohne das Projekt zu untersuchen, oder das Problem in einem Miniprojekt nachstellen zu können.

        Überraschender ist, dass DI in einem Controller einwandfrei funktioniert, in einem anderen aber nicht.

        Dann würde ich mal schauen, was die Unterschiede sind. Code kopieren, alle Actions und anderen Individualkram rauswerfen, und die beiden dann in einen Texteditor mit Vergleichsfunktion einfügen. Der nächste Schritt wäre, je eine Text-Suche über das Projekt oder Solution mit den Namen des Controllers zu starten und die Ergebnisse zu vergleichen.

        dedlfix.

      2. Du müsstest vielleicht noch etwas mehr über den Kontext erzählen, in dem Du unterwegs bist. ASP.NET MVC ist eine Annahme (basierend auf MyController), muss es aber nicht sein.

        Grundsätzlich ist es so, dass C# keinen parameterlosen Konstruktor mehr generiert, sobald Du einen eigenen Konstruktor schreibst. Deswegen fehlt er jetzt und das Framework regt sich drüber auf, weil es auf DI erstmal nicht ausgelegt ist und nach einem parameterlosen .ctor sucht.

        Um beispielsweise ASP.NET MVC Unity-fähig zu machen, brauchst Du eine entsprechende Unterstützung im Framework. Wie die bei MVC und Unity genau aussieht, hängt von den verwendeten MVC- und Unity-Versionen ab und davon, ob Du .net Classic oder .net Core verwendest. Onkel Bing Googlesby weiß dazu eine Menge Antworten; es gibt fertige Lösungen für viele Szenarien, die Du nur als nuget-Paket reinziehen musst (oder bower oder foo packet manager 4.8.15 oder oceanic packager 16.23™).

        ICH mach das allerdings anders, wenn ich mal in MVC oder Web API unterwegs bin. Ich verwende Controller extrem dünnschichtig und ohne DI. In den Actions erzeuge ich Instanzen von Worker-Klassen, die die Aufgabe der Action durchführen und im Prinzip nichts vom verwendeten Framework wissen. Wenn ich Framework-Informationen in die Action-Worker hineingeben muss, dann über eine (injizierte) Adapterklasse. Auf diese Weise ist mein Worker komplett von MVC entkoppelt und ich kann ihn prima unittesten, ohne irgendwas von ASP.NET faken zu müssen.

        D.h. meine Controller routen lediglich die Aufrufe an die Worker weiter, sonst nichts. Vermutlich könnte ich mir dafür T4-Templates schreiben, wenn ich denn den Nerv hätte, mich in T4 einzuarbeiten.

        Rolf

      3. Überraschender ist, dass DI in einem Controller einwandfrei funktioniert, in einem anderen aber nicht.

        Wie jetzt - im gleichen Projekt? Beide Controller mit der gleichen Superklasse?

        Wenn es einmal MVC und einmal Web-API wäre, dann ist es logisch, die haben unterschiedliche Factories.

        Mit der Konsolen-Anwendung hast Du kein Framework mehr im Spiel - wie instanziierst Du denn da den Controller?

        Gruß_Rolf_

        1. Hallo,

          ja, das sind zwei Controller, MVC und Api-Controller, in einem Projekt. Was spricht da dagegen. Man kann in einem Projekt unterschiedliche Cotnroller haben. Mein MVC-Controller ist da, um nur die Views aufzurufen. Api ist nur für die CRUD-Funktionalitäten da. Natürlich könnte man alles in MVC erledigen. Ich habe das in diesem besonderen Fall so gelöst.

          Das Problem ist nur, wie komme ich bei DI an den Parameterbehafteten Kontruktor, wenn den Parameterlosen zuerst aufgerufen wird?

          Gruß

          1. Tach!

            ja, das sind zwei Controller, MVC und Api-Controller, in einem Projekt. Was spricht da dagegen.

            Nichts. Du musst sie nur unterschiedlich behandeln, wenn sie unterschiedlich sind.

            Das Problem ist nur, wie komme ich bei DI an den Parameterbehafteten Kontruktor, wenn den Parameterlosen zuerst aufgerufen wird?

            Woraus schließt du, dass das DI hier das Problem ist? Wenn du zum Beispiel Code scheibst, der eine Klasse instantiiert, dann kann sich da kein DI einklinken und die Erstellung vornehmen. Dein Code muss stattdessen so ausgelegt sein, dass er die Instantiierung dem DI überlässt. Ansonsten ist das DI machtlos. Deshalb muss das DI-Framework wissen, dass und wie es sich in die Instantiierung der Controller (und gegebenenfalls anderer Klassen) einzuklinken hat. Üblicherweise gibt es da für bestimmte Szenarien (in deinem Fall ASP.NET MVC) eine Erweiterung des DI, in einem meist auch extra zu installierendem NuGet-Paket, sowie eine Anleitung, wie sich das DI in das Szenario einfügen kann. ASP.NET MVC ist jedenfalls vorbereitet für die Nutzung von DI.

            Wenn das grundlegend gegeben ist, und wenn dann das DI die Instantiierung nicht selbständig erledigen kann, dann muss man da was konfigurieren. Sprich, man fügt an der dafür vorgesehenen Stelle im Initialisierungsprozess des DI Code hinzu, der für ein bestimmtes Interface die Instantiierung eines konkreten Objekts vornimmt.

            All das muss die Dokumentation des DI-Frameworks hergeben.

            dedlfix.

            1. Hallo,

              danke für die Antwort. Um das Ganze etwas klarer wird, versuche ich eine paar Zeil mehr Code zu schreiben. Ich hoffe, es hilft:

              Das ist mein Api Kontroller:

                 public class MyController : ApiController
                  {
                      private readonly IShop _shop;
              
                      public MyController(IShop shop)
                      {
                           _shop = shop;
                      }
              
                      public IEnumerable<MyModel> Get(int id)
                      {
                           var car = _shop.GetCarModel(id);
                           return View(car);
                      }
                   }
              

              und das mein MVC-Controller:

                  public class HomeController : Controller
                  {
                      public ActionResult Edit(int id)
                      {
                          using (ShopClient client = new ShopClient())
                          {
                              var car = carClient.GetCarModel(id);
                              return View(car);
                          }
                      }
                  }
              

              Wie man sieht, machen die Methoden Get() und Edit() beide das gleiche. Mit einem Unterschied, dass die Edit-Methode über ein new verfügt, das ich loswerden möchte. Nun habe ich versucht, von da aus direkt auf die Get()-Methode des Api-Controller zuzugreifen, um mein Ziel mit der new-Beseitigung zu erreichen. Dazu habe ich dies versuchet:

              var apiController = DependencyResolver.Current.GetService<MyController>();
              var test = apiController.Get(2);
              

              Dieser Ansatz funktioniert leider mit Api-Controllern nicht und ich bekomme ein null zurück.

              Gruß

              1. Tach!

                Um das Ganze etwas klarer wird, versuche ich eine paar Zeil mehr Code zu schreiben. Ich hoffe, es hilft:

                Mir nicht, weil ich mich dazu erstmal in Unity und das Zusammenspiel zwischen Unity und ASP.NET MVC einlesen müsste. Ich habe aber mit den Stichwörtern "unity asp.net mvc api controller" Dokumentation dazu auf den Microsoft-Webseiten gefunden.

                Wie man sieht, machen die Methoden Get() und Edit() beide das gleiche. Mit einem Unterschied, dass die Edit-Methode über ein new verfügt, das ich loswerden möchte.

                var apiController = DependencyResolver.Current.GetService<MyController>();
                var test = apiController.Get(2);
                

                Wenn du das nicht nur zum Test geschrieben hast sondern vorhast, lediglich das new durch einen Resolver-Aufruf zu ersetzen, dann ist das ein Unterfangen, das nicht besonders sinnvoll ist, um das Ziel der lockeren Kopplung zu erreichen. Wenn es nur zum Test ist, dann ignoriert diesen Teil.

                Dieser Ansatz funktioniert leider mit Api-Controllern nicht und ich bekomme ein null zurück.

                Ist denn das Unity so konfiguriert, dass es sich auch um die API-Controller kümmert?

                dedlfix.

                1. Es gibt zwei Unities, eins für MVC5 und eins für WebAPI. Die RegisterComponents der beiden sind auch etwas anders. Daher hatte ich Probleme mit Api als ich das Unity-MVC5-Paket installiert hatte und nun umgekehrt, wo ich Unity-WebApi installiert habe.

                  1. Moin,

                    ich war das WE verreist, drum schreibe ich jetzt erst wieder.

                    Es gibt nur ein Unity, und es weiß überhaupt nichts von MVC oder API oder WebForms oder sonstwas. Es weiß nur was von Dependencies und kann sie automatisch einspritzen.

                    MVC und Web API wissen dagegen was von Controllern und haben Discovery-Techniken und Controller-Factories, um das "Convention before Configuration" Prinzip umzusetzen. Irgendein Döspaddel bei den ASP.NET Architekten hat es aber versäumt, hier von Anfang an auf Einheitlichkeit zu achten - oder es gab technische Gründe, warum es nicht anders ging - und deshalb sind es getrennte Factories, die man auch getrennt für Unity aufpimpen muss.

                    Das kann man mit fertigen Paketen tun, oder man kann es auch selbst programmieren.

                    Für MVC Controller brauchst Du eine Implementierung von IDependencyResolver, die Du bei DependencyController.Current hinterlegst. Diesess Interface befindet sich im System.Web.Mcv Namespace. Ein MVC-to-Unity Adapter muss sich hier einklinken, und darüber entweder einen IControllerActivator oder eine IControllerFactory bereitstellen. Der IControllerActivator ist die bevorzugte Lösung. Das MVC5-Unity Paket dürfte einen IControllerActivator enthalten. Details dazu kannst Du googeln :)

                    Für das Web.API brauchst Du auch eine IDependencyResolver Implementierung. Aber - wie gesagt - es ist eine andere. Es ist ein ANDERES Interface mit gleichem Namen, im System.Web.Http.Dependencies Namespace, und Du hinterlegst die Implementierung in GlobalConfiguration.Configuration.DependencyResolver.

                    Wenn Du MVC und WebAPI gemeinsam verwenden willst, musst Du beide Dependency-Resolver bereitstellen und beide auf die ihnen genehme Art registrieren. Wenn es dafür kein fertiges Nuget-Paket gibt, musst Du es von Hand mischen. Am Ende hast Du dann drei Bausteine: Unity selbst, den MVC-to-Unify Adapter und den WebAPI-to-Unity Adapter.

                    Rolf