Ich versuche, eine MVC4 / MVC5-Anwendung mit einer steckbaren Architektur wie Orchard CMS zu erstellen. Ich habe also eine MVC-Anwendung, die das Startprojekt sein wird und sich um Authentifizierung, Navigation usw. kümmert. Dann werden mehrere Module separat als asp.net-Klassenbibliotheken oder abgespeckte MVC-Projekte erstellt und verfügen über Controller, Ansichten, Datenrepos usw.
Ich habe den ganzen Tag Tutorials im Internet durchgesehen und Beispiele usw. heruntergeladen und festgestellt, dass Kenny das beste Beispiel dafür hat - http://kennytordeur.blogspot.in/2012/08/mef-in-aspnet-mvc-4-and -webapi.html
Ich kann die Controller aus den Modulen importieren (separate DLLs), wenn ich Verweise auf diese DLLs hinzufüge. Der Grund für die Verwendung von MEF ist jedoch, dass zur Laufzeit Module hinzugefügt werden können. Ich möchte, dass die DLLs zusammen mit den Ansichten in ein Verzeichnis ~ / Modules // im Startprojekt kopiert werden (dies ist mir gelungen), und MEF würde sie einfach abholen. Es fällt mir schwer, MEF dazu zu bringen, diese Bibliotheken zu laden.
Es gibt auch MefContrib, wie in dieser Antwort erläutert. ASP.NET MVC 4.0-Controller und MEF, wie können diese beiden zusammengeführt werden? Das ist das nächste, was ich versuchen werde. Aber ich bin überrascht, dass MEF mit MVC nicht sofort funktioniert.
Hat jemand eine ähnliche Architektur (mit oder ohne MefContrib)? Anfangs dachte ich sogar daran, Orchard CMS zu entfernen und als Framework zu verwenden, aber es ist zu komplex. Es wäre auch schön, die App in MVC5 zu entwickeln, um WebAPI2 zu nutzen.
Antworten:
Ich habe an einem Projekt gearbeitet, das eine ähnliche steckbare Architektur wie die von Ihnen beschriebene hatte und die gleichen Technologien wie ASP.NET MVC und MEF verwendete. Wir hatten eine ASP.NET MVC-Hostanwendung, die die Authentifizierung, Autorisierung und alle Anforderungen abwickelte. Unsere Plugins (Module) wurden in einen Unterordner kopiert. Die Plugins waren auch ASP.NET MVC-Anwendungen mit eigenen Modellen, Controllern, Ansichten, CSS- und JS-Dateien. Dies sind die Schritte, die wir befolgt haben, damit es funktioniert:
MEF einrichten
Wir haben eine auf MEF basierende Engine erstellt, die beim Start der Anwendung alle zusammensetzbaren Teile erkennt und einen Katalog der zusammensetzbaren Teile erstellt. Dies ist eine Aufgabe, die beim Start der Anwendung nur einmal ausgeführt wird. Die Engine muss alle steckbaren Teile erkennen, die sich in unserem Fall entweder im
bin
Ordner der Host-Anwendung oder imModules(Plugins)
Ordner befanden .public class Bootstrapper { private static CompositionContainer CompositionContainer; private static bool IsLoaded = false; public static void Compose(List<string> pluginFolders) { if (IsLoaded) return; var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"))); foreach (var plugin in pluginFolders) { var directoryCatalog = new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", plugin)); catalog.Catalogs.Add(directoryCatalog); } CompositionContainer = new CompositionContainer(catalog); CompositionContainer.ComposeParts(); IsLoaded = true; } public static T GetInstance<T>(string contractName = null) { var type = default(T); if (CompositionContainer == null) return type; if (!string.IsNullOrWhiteSpace(contractName)) type = CompositionContainer.GetExportedValue<T>(contractName); else type = CompositionContainer.GetExportedValue<T>(); return type; } }
Dies ist der Beispielcode der Klasse, die die Ermittlung aller MEF-Teile durchführt. Die
Compose
Methode der Klasse wird von derApplication_Start
Methode in derGlobal.asax.cs
Datei aufgerufen . Der Code wird der Einfachheit halber reduziert.public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { var pluginFolders = new List<string>(); var plugins = Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules")).ToList(); plugins.ForEach(s => { var di = new DirectoryInfo(s); pluginFolders.Add(di.Name); }); AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); Bootstrapper.Compose(pluginFolders); ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory()); ViewEngines.Engines.Add(new CustomViewEngine(pluginFolders)); } }
Es wird davon ausgegangen, dass alle Plugins in einen separaten Unterordner des Ordners kopiert werden
Modules
, der sich im Stammverzeichnis der Hostanwendung befindet. Jeder Plugin-Unterordner enthält denViews
Unterordner und die DLL von jedem Plugin. In derApplication_Start
obigen Methode werden auch die benutzerdefinierte Controller-Factory und die benutzerdefinierte Ansichts-Engine initialisiert, die ich unten definieren werde.Erstellen einer Controller-Factory, die von MEF liest
Hier ist der Code zum Definieren der benutzerdefinierten Controller-Factory, die den Controller erkennt, der die Anforderung verarbeiten muss:
public class CustomControllerFactory : IControllerFactory { private readonly DefaultControllerFactory _defaultControllerFactory; public CustomControllerFactory() { _defaultControllerFactory = new DefaultControllerFactory(); } public IController CreateController(RequestContext requestContext, string controllerName) { var controller = Bootstrapper.GetInstance<IController>(controllerName); if (controller == null) throw new Exception("Controller not found!"); return controller; } public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { return SessionStateBehavior.Default; } public void ReleaseController(IController controller) { var disposableController = controller as IDisposable; if (disposableController != null) { disposableController.Dispose(); } } }
Zusätzlich muss jeder Controller mit folgenden
Export
Attributen gekennzeichnet sein:[Export("Plugin1", typeof(IController))] [PartCreationPolicy(CreationPolicy.NonShared)] public class Plugin1Controller : Controller { // // GET: /Plugin1/ public ActionResult Index() { return View(); } }
Der erste Parameter des
Export
Attributkonstruktors muss eindeutig sein, da er den Vertragsnamen angibt und jeden Controller eindeutig identifiziert. DasPartCreationPolicy
muss auf NonShared gesetzt sein, da Controller nicht für mehrere Anforderungen wiederverwendet werden können.Erstellen einer View Engine, die die Ansichten der Plugins findet
Die Erstellung einer benutzerdefinierten Ansichts-Engine ist erforderlich, da die Ansichts-Engine gemäß Konvention nur im
Views
Ordner der Host-Anwendung nach Ansichten sucht . Da sich die Plugins in einem separatenModules
Ordner befinden, müssen wir der View Engine mitteilen, dass sie auch dort suchen soll.public class CustomViewEngine : RazorViewEngine { private List<string> _plugins = new List<string>(); public CustomViewEngine(List<string> pluginFolders) { _plugins = pluginFolders; ViewLocationFormats = GetViewLocations(); MasterLocationFormats = GetMasterLocations(); PartialViewLocationFormats = GetViewLocations(); } public string[] GetViewLocations() { var views = new List<string>(); views.Add("~/Views/{1}/{0}.cshtml"); _plugins.ForEach(plugin => views.Add("~/Modules/" + plugin + "/Views/{1}/{0}.cshtml") ); return views.ToArray(); } public string[] GetMasterLocations() { var masterPages = new List<string>(); masterPages.Add("~/Views/Shared/{0}.cshtml"); _plugins.ForEach(plugin => masterPages.Add("~/Modules/" + plugin + "/Views/Shared/{0}.cshtml") ); return masterPages.ToArray(); } }
Lösen Sie das Problem mit stark typisierten Ansichten in den Plugins
Wenn wir nur den obigen Code verwenden, können wir in unseren Plugins (Modulen) keine stark typisierten Ansichten verwenden, da Modelle außerhalb des
bin
Ordners vorhanden sind. Um dieses Problem zu lösen, folgen Sie dem folgenden Link .quelle
Beachten Sie jedoch, dass der MEF-Container über eine "nette Funktion" verfügt, die Verweise auf jedes von ihm erstellte IDisposable-Objekt enthält und zu einem großen Speicherverlust führt. Angeblich kann der Speicherverlust mit diesem Nuget behoben werden - http://nuget.org/packages/NCode.Composition.DisposableParts.Signed
quelle
Es gibt Projekte, die eine Plugin-Architektur implementieren. Vielleicht möchten Sie eine davon verwenden oder sich den Quellcode ansehen, um zu sehen, wie sie diese Dinge erreichen:
Auch 404 über Steuerungen in externen Baugruppen verfolgt einen interessanten Ansatz. Ich habe viel gelernt, indem ich nur die Frage gelesen habe.
quelle