J'ai travaillé sur un projet qui avait le même type d'architecture enfichable comme celle que vous avez décrite, et il a utilisé les mêmes technologies ASP.NET MVC
et MEF
. Nous avons eu un accueil ASP.NET application MVC qui s'occupait de l'authentification, l'autorisation et toutes les demandes. Nos plug-ins(modules) ont été copiés dans un sous-dossier. Les plugins ont également été ASP.NET MVC
applications qui avait ses propres modèles, les contrôleurs, les vues, les fichiers css et js. Ce sont les étapes que nous avons suivies pour le faire fonctionner:
La configuration de la MEF
Nous avons créé basé sur le moteur sur MEF
qui découvre tous les composables parties au démarrage de l'application et crée un catalogue de la composable pièces. C'est une tâche qui est pefromed qu'une seule fois au démarrage de l'application. Le moteur a besoin pour découvrir toutes plugable parties, qui dans notre cas se trouvaient dans l' bin
le dossier de l'application hôte ou dans l' Modules(Plugins)
le dossier.
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;
}
}
C'est l'exemple de code de la classe qui effectue la détection de tous les MEF pièces. L' Compose
méthode de la classe est appelée à partir de l' Application_Start
méthode Global.asax.cs
le fichier. Le code est réduite pour des raisons de simplicité.
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));
}
}
Il est supposé que tous les plugins sont copiés dans un sous-dossier de l' Modules
le dossier qui se trouve dans la racine de l'application hôte. Chaque plugin sous-dossier contient Views
sous-dossier et l' dll
de chaque plugin. Dans l' Application_Start
méthode ci-dessus sont également initialisé la coutume contrôleur de l'usine et de la vue personnalisée du moteur que je vais définir ci-dessous.
La création de contrôleur de l'usine qui se lit de la MEF
Voici le code pour la définition de la coutume contrôleur de l'usine qui permettra de découvrir le contrôleur doit gérer la demande:
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();
}
}
}
En outre, chaque contrôleur doit être marqué avec Export
d'attribut:
[Export("Plugin1", typeof(IController))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Plugin1Controller : Controller
{
//
// GET: /Plugin1/
public ActionResult Index()
{
return View();
}
}
Le premier paramètre de la Export
constructeur d'attribut doit être unique, car il spécifie le nom du contrat et identifie de manière unique chaque contrôleur. L' PartCreationPolicy
doit être réglé à NonShared parce que les contrôleurs ne peuvent pas être réutilisés pour de multiples demandes.
Création de Vue Moteur qui sait trouver le point de vue de l'plugins
Création de la vue personnalisée du moteur est nécessaire parce que le moteur d'affichage par convention cherche seulement des vues dans l' Views
le dossier de l'application hôte. Depuis les plugins sont situés dans différents Modules
le dossier, nous devons le dire à la vue du moteur de regarder là-bas aussi.
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();
}
}
Résoudre le problème avec des points de vue fortement typée dans les plugins
Uniquement à l'aide du code ci-dessus, nous ne pouvions pas utiliser fortement typé vues dans nos plug-ins(modules), parce que les modèles existent en dehors de l' bin
le dossier. Pour résoudre ce problème, suivez les suivants lien.