Ich versuche, eine MVC4-Webanwendung mit einer Reihe von Plugins zu erstellen, d. h. im Wesentlichen über MEF exportierte Controller plus Inhaltsdateien, die an den richtigen Stellen entpackt wurden. Ich habe viel Material über MVC-Plugins gefunden, hauptsächlich in Bezug auf Bereiche, aber ich musste mit MvcContrib aufgeben, was die offensichtlichste Lösung wäre, da es nicht mehr entwickelt zu sein scheint, einige Probleme mit den neuesten MVC-Bits zeigt, und ich möchte auch eine minimal komplexe Implementierung für diese Architektur.
Meine Anforderungen waren also:
a) Eine MEF-basierte MVC-Plugin-Lösung, bei der ich einfach ein Paket in meine Site einlege, damit es verwendet werden kann, idealerweise ohne einen Neustart. Dies bedeutet, Plugins in einem anderen Ordner als Bin zu speichern, was auch für eine bessere Isolierung sorgt.
b) eine Lösung, die den IoC-Tools entspricht und vollständiger ist, als dies mit MEF allein möglich ist. Ich neige dazu, Autofac zu verwenden, da es sowohl mit MEF als auch MVC4 (RC zu dieser Zeit) integriert ist.
Abgesehen von Inhalten wie Views ist es die essenzielle Aufgabe, MVC Controller unter MEF-Plugins zu lokalisieren und zu instantiieren, also brauche ich eine Controller Factory . Ich habe hier einen guten Artikel gefunden: Ссылка (Ich kontaktierte Kenny darüber und ich danke ihm, dass er mich auf ein Routingproblem hingewiesen hat). Der Autor hat seinen Code auch in ein praktisches nugget-Paket (MEF.MVC4) gepackt. Wie auch immer, ich finde ein Problem, das mit Routing und Namespaces zu tun hat: Wenn die Route auf einen Plugin-Controller trifft, erhält die GetControllerInstance-Methode der MEF-Controller-Factory einen null controllerType , was schließlich zu einem 404 führt. Ich glaube, ich habe den Schuldigen gefunden, als ich diese Beiträge gelesen habe:
und
Benutzerdefinierte Controller Factory, Dependency Injection / Structuremap Probleme mit ASP .NET MVC
Ich nehme an (aber ich könnte falsch liegen), dass das Problem in Routing-Konventionen und Plugin-Bereich-Controller-Namespaces liegt: Der Namespace des Plugin-Controllers befindet sich nicht im gleichen "root" des Host-Webs. Die im Post vorgeschlagene Lösung ist nur das Hinzufügen einer neuen Route zu der Web-App, aber dies passt nicht in eine Lösung, bei der Bereiche als Plug-Ins funktionieren, die dynamisch zur Host-Anwendung hinzugefügt werden. Meine Host-Web-App muss sich der Plugins nicht bewusst sein, und das sollte natürlich eine ziemlich häufige Anforderung sein, aber ich finde keine offensichtliche Lösung dafür.
Sie können schnell eine Repro-Lösung erstellen, so dass Sie die Details meiner Vorgehensweise sehen können, indem Sie diese Schritte ausführen oder sie von hier :
1) Erstellen Sie eine leere Lösung.
2) Erstellen Sie eine MVC4-Webanwendung (HostWeb), aktualisieren Sie alle vorinstallierten NuGet-Pakete und fügen Sie Mef.MVC4, Autofac MVC 4 (RC) und Autofac.Mef hinzu. In meiner Real-World-App möchte ich Autofac verwenden, um Controller-Abhängigkeiten in Konstruktor zu injizieren.
3) Erstellen Sie einen Plugins-Ordner in HostWeb und einen Unterordner Temp. Dies schließt Plugins ein, die den Temp-Unterordner als Schattenkopien-Container verwenden, so dass der MEF-Katalog von diesem geladen wird und nicht direkt von Plugins. Dies sollte in Verbindung mit etwas Startup-Code mir erlauben, die Plugins zu aktualisieren, ohne die Web-App neu starten zu müssen (was sonst die DLLs sperren würde). Der Startcode ist eine Klasse mit dem Namen PreApplicationInit, die Sie im Ordner "Infrastructure" (leicht modifiziert von Ссылка ).
4) Fügen Sie dem Host-Web einen Teilebereich hinzu und kopieren Sie die _ViewStart-Datei aus dem Stammordner der Ansichten (und ändern Sie vorhandene Verknüpfungen in der Layout-Ansicht, sodass den Routenwerten ein leerer Bereich hinzugefügt wird, damit sie nicht gelöscht werden) kaputt gehen). Alle Plugin-Controller werden in einem Bereich mit dem Namen Teile benannt. Die Host-Webanwendung hat einen solchen Bereich ohne Controller, nur um die Ordnerstruktur und Routen für Plugin-Inhaltsdateien vorzubereiten (Ansichten, die von einem Plugin-Installer-Modul an den richtigen Ort entpackt werden, während Binärdateien in Plugins platziert werden).
5) in App_Start von HostWeb anpassen MefConfig und fügen Sie IocConfig, die Autofac behandelt. Fügen Sie dann global asax die Aufrufe von beiden hinzu: MefConfig.RegisterMef () und IocConfig.RegisterDependencies ().
6) Erstellen Sie eine weitere MVC4-Webanwendung (AlphaPlugin), aktualisieren Sie alle vorinstallierten NuGet-Pakete und fügen Sie Autofac MVC 4 (RC) und Autofac.Mef hinzu. Ich wähle eine Web-App-Vorlage (anstelle einer Klassenbibliothek), damit ich alle VS-Funktionen für MVC verwenden und eventuell direkt dort testen kann.
7) Fügen Sie dem Host-Web einen Teilebereich hinzu und kopieren Sie die _ViewStart-Datei in den Stammordner der Ansichten.
8) Fügen Sie einen exportierbaren Controller in den Bereich Parts ein. Meine heißt AlphaController und hat nur eine Aktionsmethode namens Hail, die in den ViewBag eine Zeichenfolge einfügt und die Standardansicht zurückgibt.
9) zurück zum Host, fügen Sie einfach einen Link zur Aktion des Plugin-Controllers in der Home-Ansicht hinzu, um zu testen, ob der Zugriff über MEF möglich ist.
Wenn ich jetzt alles erstelle und die AlphaPlugin.dll-Binärdatei in den HostWeb-Plugins-Ordner kopiere, erwarte ich, dass MVC es über MEF findet, aber dann einen View not found error, da ich noch keine Inhaltsdateien kopiert habe das Host-Web. Stattdessen bekomme ich Folgendes:
%Vor%Hier ist der relevanteste Code (Sie können alles in der Repro-Lösung finden): Dies betrifft Plugins-Ordner-Inhalte, so dass die Web-App sie aus einer Kopie lädt:
%Vor%Und das ist meine Fabrik, die sowieso einen null controllerType bekommt, so dass ihr Code nie über die erste Zeile hinaus ausgeführt wird:
%Vor%Die Steuerungsfabrik benötigt eine Hand, um den richtigen Steuerungstyp für externe MEF-Komponenten zu finden. Überschreibe die GetControllerType-Methode der MefControllerFactory-Klasse wie folgt.
%Vor%Dabei ist IControllerMetaData eine Schnittstelle, die den Namen des Controllers angibt
%Vor%Und Ihr Controller gibt den ControllerName in den Metadaten an. ZB
%Vor%Tags und Links autofac asp.net-mvc-4 mef asp.net-mvc-areas