80 votes

MEF avec MVC 4 ou 5 - Architecture Enfichable (2014)

Je suis en train de construire un MVC4/MVC5 application avec une architecture enfichable comme le Verger de la CMS. J'ai donc une application MVC qui sera le démarrage du projet et de prendre soin de auth, de navigation, etc. Ensuite, il y aura plusieurs modules compilés séparément asp.net les bibliothèques de classes ou dépouillé mvc projets et d'avoir des contrôleurs, des vues, des données de repos etc.

J'ai passé toute la journée à travers des tutoriels sur le web et télécharger des échantillons etc et a constaté que Kenny est le meilleur exemple autour http://kennytordeur.blogspot.in/2012/08/mef-in-aspnet-mvc-4-and-webapi.html

Je suis en mesure d'importer les contrôleurs de modules(séparé de la Dll) si j'ajoute la référence à ces Dll. Mais la raison derrière l'utilisation de la MEF est de pouvoir ajouter des modules au moment de l'exécution. Je veux que la Dll avec des vues à être copié sur un ~/Modules// répertoire dans le projet de démarrage (j'ai réussi à le faire) et MEF serait que juste les ramasser. La difficulté à faire des MEF charge de ces bibliothèques.

Il est également MefContrib comme expliqué dans cette réponse ASP.NET MVC 4.0 Contrôleurs et MEF, comment amener ces deux ensemble? qui est la prochaine chose que je suis sur le point d'essayer. Mais je suis surpris que le MEF ne pas travailler hors de la boîte avec MVC.

Quiconque a une architecture similaire de travail (avec ou sans MefContrib)? Ou connaissez-vous un cadre existant que j'ai pu regarder? Au départ, j'ai même pensé que le décapage de Verger de la CMS et de l'utiliser comme un cadre, mais il est trop complexe. Aussi serait bien de développer l'application dans MVC5 pour profiter de WebAPI2.

105voto

Ilija Dimov Points 1902

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X