Ich habe einige Zeit damit verbracht, mir Phil Haacks Artikel über das Gruppieren von Controllern mit sehr interessanten Dingen anzusehen.
Im Moment versuche ich herauszufinden, ob es möglich wäre, dieselben Ideen zu verwenden, um eine Plug-in / modulare Architektur für ein Projekt zu erstellen, an dem ich arbeite.
Meine Frage lautet also: Ist es möglich, die Bereiche in Phils Artikel auf mehrere Projekte aufzuteilen?
Ich kann sehen, dass die Namensräume von selbst funktionieren, aber ich bin besorgt darüber, dass die Ansichten an der richtigen Stelle landen. Ist es etwas, das mit Build-Regeln aussortiert werden kann?
Unter der Annahme, dass das oben Genannte mit mehreren Projekten in einer einzigen Lösung möglich ist, hat jemand Ideen, wie dies mit einer separaten Lösung und Codierung für einen vordefinierten Satz von Schnittstellen am besten möglich ist? Wechseln von einem Bereich zu einem Plug-In.
Ich habe einige Erfahrungen mit Plug-In-Architekturen, aber keine Massen, daher wäre jede Anleitung in diesem Bereich nützlich.
quelle
Ich arbeite derzeit an einem Erweiterungsframework, das zusätzlich zu ASP.NET MVC verwendet werden kann. Mein Erweiterbarkeits-Framework basiert auf dem berühmten Ioc-Container: Structuremap.
Der Anwendungsfall, den ich zu erfüllen versuche, ist einfach: Erstellen Sie eine Anwendung mit einigen grundlegenden Funktionen, die für jeden Kunden erweitert werden können (= Mandantenfähigkeit). Es sollte nur eine Instanz der Anwendung gehostet werden, aber diese Instanz kann für jeden Kunden angepasst werden, ohne Änderungen an der Kernwebsite vorzunehmen.
Ich wurde von dem Artikel über Multitätigkeit von Ayende Rahien inspiriert: http://ayende.com/Blog/archive/2008/08/16/Multi-Tenancy--Approaches-and-Applicability.aspx Eine weitere Inspirationsquelle war die Buch von Eric Evans über Domain Driven Design. Mein Extensibility-Framework basiert auf dem Repository-Muster und dem Konzept der Root-Aggregate. Um das Framework verwenden zu können, sollte die Hosting-Anwendung auf Repositorys und Domänenobjekten basieren. Die Controller, Repositorys oder Domänenobjekte werden zur Laufzeit von der ExtensionFactory gebunden.
Ein Plug-In ist einfach eine Zusammenstellung, die Controller oder Repositorys oder Domänenobjekte enthält, die eine bestimmte Namenskonvention einhalten. Die Namenskonvention ist einfach. Jeder Klasse sollte die Kunden-ID vorangestellt werden, z. B.: AdventureworksHomeController.
Um eine Anwendung zu erweitern, kopieren Sie eine Plug-In-Assembly in den Erweiterungsordner der Anwendung. Wenn ein Benutzer eine Seite im Stammverzeichnis des Kunden anfordert, z. B.: Http://multitenant-site.com/[customerID‹/[controller‹/[action], überprüft das Framework, ob für diesen bestimmten Kunden ein Plug-In vorhanden ist, und instanziiert die benutzerdefinierten Plug-In-Klassen, andernfalls wird der Standard einmal geladen. Die benutzerdefinierten Klassen können Controller - Repositorys oder Domänenobjekte sein. Dieser Ansatz ermöglicht die Erweiterung einer Anwendung auf allen Ebenen, von der Datenbank bis zur Benutzeroberfläche, über das Domänenmodell und die Repositorys.
Wenn Sie einige vorhandene Funktionen erweitern möchten, erstellen Sie ein Plug-In für eine Assembly, die Unterklassen der Kernanwendung enthält. Wenn Sie völlig neue Funktionen erstellen müssen, fügen Sie dem Plug-In neue Controller hinzu. Diese Controller werden vom MVC-Framework geladen, wenn die entsprechende URL angefordert wird. Wenn Sie die Benutzeroberfläche erweitern möchten, können Sie eine neue Ansicht im Erweiterungsordner erstellen und auf die Ansicht eines neuen oder untergeordneten Controllers verweisen. Um das vorhandene Verhalten zu ändern, können Sie neue Repositorys oder Domänenobjekte erstellen oder vorhandene Unterklassen unterordnen. Die Framework-Verantwortung besteht darin, zu bestimmen, welcher Controller / Repository / Domänenobjekt für einen bestimmten Kunden geladen werden soll.
Ich empfehle einen Blick auf die Strukturkarte ( http://structuremap.sourceforge.net/Default.htm)) und insbesondere bei der Registrierung DSL-Funktionen http://structuremap.sourceforge.net/RegistryDSL.htm .
Dies ist der Code, den ich beim Start der Anwendung verwende, um alle Plug-in-Controller / Repositorys oder Domänenobjekte zu registrieren:
protected void ScanControllersAndRepositoriesFromPath(string path) { this.Scan(o => { o.AssembliesFromPath(path); o.AddAllTypesOf<SaasController>().NameBy(type => type.Name.Replace("Controller", "")); o.AddAllTypesOf<IRepository>().NameBy(type => type.Name.Replace("Repository", "")); o.AddAllTypesOf<IDomainFactory>().NameBy(type => type.Name.Replace("DomainFactory", "")); }); }
Ich verwende auch eine ExtensionFactory, die von System.Web.MVC erbt. DefaultControllerFactory. Diese Factory ist dafür verantwortlich, die Erweiterungsobjekte (Controller / Registries oder Domänenobjekte) zu laden. Sie können Ihre eigenen Fabriken anschließen, indem Sie sie beim Start in der Datei Global.asax registrieren:
protected void Application_Start() { ControllerBuilder.Current.SetControllerFactory( new ExtensionControllerFactory() ); }
Dieses Framework als voll funktionsfähige Beispielsite finden Sie unter: http://code.google.com/p/multimvc /
quelle
Also habe ich ein bisschen mit dem Beispiel von J Wynia oben herumgespielt . Vielen Dank dafür übrigens.
Ich habe die Dinge so geändert, dass die Erweiterung des VirtualPathProvider einen statischen Konstruktor verwendete, um eine Liste aller verfügbaren Ressourcen zu erstellen, die mit .aspx in den verschiedenen DLLs im System enden. Es ist mühsam, aber wir machen es nur einmal.
Es ist wahrscheinlich ein totaler Missbrauch der Art und Weise, wie VirtualFiles auch verwendet werden sollen ;-)
Sie erhalten:
privates statisches IDictionary resourceVirtualFile;
wobei die Zeichenfolge virtuelle Pfade sind.
Der folgende Code macht einige Annahmen über den Namespace der ASPX-Dateien, funktioniert jedoch in einfachen Fällen. Das Schöne daran ist, dass Sie keine komplizierten Ansichtspfade erstellen müssen, die aus dem Ressourcennamen erstellt werden.
class ResourceVirtualFile : VirtualFile { string path; string assemblyName; string resourceName; public ResourceVirtualFile( string virtualPath, string AssemblyName, string ResourceName) : base(virtualPath) { path = VirtualPathUtility.ToAppRelative(virtualPath); assemblyName = AssemblyName; resourceName = ResourceName; } public override Stream Open() { assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName + ".dll"); Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyName); if (assembly != null) { Stream resourceStream = assembly.GetManifestResourceStream(resourceName); if (resourceStream == null) throw new ArgumentException("Cannot find resource: " + resourceName); return resourceStream; } throw new ArgumentException("Cannot find assembly: " + assemblyName); } //todo: Neaten this up private static string CreateVirtualPath(string AssemblyName, string ResourceName) { string path = ResourceName.Substring(AssemblyName.Length); path = path.Replace(".aspx", "").Replace(".", "/"); return string.Format("~{0}.aspx", path); } public static IDictionary<string, VirtualFile> FindAllResources() { Dictionary<string, VirtualFile> files = new Dictionary<string, VirtualFile>(); //list all of the bin files string[] assemblyFilePaths = Directory.GetFiles(HttpRuntime.BinDirectory, "*.dll"); foreach (string assemblyFilePath in assemblyFilePaths) { string assemblyName = Path.GetFileNameWithoutExtension(assemblyFilePath); Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyFilePath); //go through each one and get all of the resources that end in aspx string[] resourceNames = assembly.GetManifestResourceNames(); foreach (string resourceName in resourceNames) { if (resourceName.EndsWith(".aspx")) { string virtualPath = CreateVirtualPath(assemblyName, resourceName); files.Add(virtualPath, new ResourceVirtualFile(virtualPath, assemblyName, resourceName)); } } } return files; } }
Im erweiterten VirtualPathProvider können Sie dann Folgendes tun:
private bool IsExtended(string virtualPath) { String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return resourceVirtualFile.ContainsKey(checkPath); } public override bool FileExists(string virtualPath) { return (IsExtended(virtualPath) || base.FileExists(virtualPath)); } public override VirtualFile GetFile(string virtualPath) { string withTilda = string.Format("~{0}", virtualPath); if (resourceVirtualFile.ContainsKey(withTilda)) return resourceVirtualFile[withTilda]; return base.GetFile(virtualPath); }
quelle
Ich denke, es ist möglich, Ihre Ansichten in den Plug-In-Projekten zu belassen.
Das ist meine Idee: Sie benötigen eine ViewEngine, die das Plugin (wahrscheinlich über eine Schnittstelle) aufruft und die Ansicht anfordert (IView). Das Plugin instanziiert die Ansicht dann nicht über seine URL (wie es eine normale ViewEngine tut - /Views/Shared/View.asp), sondern über den Namen der Ansicht), beispielsweise über Reflection oder DI / IoC-Container.
Die Rückgabe der Ansicht im Plugin könnte mich sogar fest codieren (einfaches Beispiel folgt):
public IView GetView(string viewName) { switch (viewName) { case "Namespace.View1": return new View1(); case "Namespace.View2": return new View2(); ... } }
... das war nur eine Idee, aber ich hoffe, es könnte funktionieren oder nur eine gute Inspiration sein.
quelle
Dieser Beitrag ist vielleicht etwas spät, aber ich habe mit ASP.NET MVC2 gespielt und einen Prototyp mit der Funktion "Bereiche" entwickelt.
Hier ist der Link für alle Interessierten: http://www.veebsbraindump.com/2010/06/asp-net-mvc2-plugins-using-areas/
quelle
[als Antwort posten, weil ich keinen Kommentar abgeben kann]
Tolle Lösung - Ich habe den Ansatz von J Wynia verwendet und ihn dazu gebracht, eine Ansicht von einer separaten Baugruppe zu rendern. Dieser Ansatz scheint jedoch nur die Ansicht zu rendern. Controller innerhalb des Plugins scheinen nicht unterstützt zu werden, richtig? Zum Beispiel, wenn eine Ansicht von einem Plugin einen Beitrag zurück tat, dass der Controller die Ansichten innerhalb des Plugins wird nicht aufgerufen werden . Stattdessen wird es an einen Controller in der Root-MVC-Anwendung weitergeleitet . Verstehe ich das richtig oder gibt es eine Problemumgehung für dieses Problem?
quelle