Share via



Septembre 2016

Volume 31, numéro 9

Cet article a fait l'objet d'une traduction automatique.

ASP.NET Core - Feature Slices pour ASP.NET Core MVC

Par Steve Smith

Les applications Web volumineuses nécessitent une meilleure organisation que les réseaux de petite taille. Avec des applications volumineuses, la structure d’organisation par défaut utilisée par ASP.NET MVC (et Core MVC) commence à travailler avec vous. Vous pouvez utiliser deux techniques simples pour mettre à jour votre approche d’organisation et suivre une application dont la croissance.

Le modèle Model-View-Controller (MVC) est mature, même dans l’espace de Microsoft ASP.NET. La première version d’ASP.NET MVC est livré en 2009 et le premier redémarrage complet de la plateforme, ASP.NET MVC de base, fourni au début de cet été. Tout au long de ce temps, comme ASP.NET MVC a évolué, la structure de projet par défaut n’a pas changée : dossiers pour les contrôleurs et les vues et souvent pour les modèles (ou peut-être ViewModel). En fait, si vous créez une application ASP.NET Core dès aujourd'hui, vous verrez ces dossiers créés par le modèle par défaut, comme indiqué dans Figure 1.

Structure de modèle d’application Web par défaut ASP.NET Core
Figure 1 par défaut la Structure de modèle d’application Web ASP.NET Core

Il existe de nombreux avantages de cette structure organisationnelle. Il vous est familier ; Si vous avez travaillé sur un projet ASP.NET MVC dans ces dernières années, vous allez le reconnaît immédiatement. Elle est organisée ; Si vous recherchez un contrôleur ou une vue, vous avez une bonne idée où commencer. Lorsque vous vous commencez un nouveau projet, ce fonctionnement de la structure organisationnelle bien gérée, car il n’existe pas encore de fichiers. À mesure que le projet augmente, toutefois, ne la friction impliquées à localiser le fichier souhaité sur le contrôleur ou une vue dans le nombre croissant de fichiers et dossiers dans ces hiérarchies.

Pour voir ce que je veux dire, imaginez que si vous avez organisé vos fichiers d’ordinateur dans cette même structure. Au lieu d’avoir des dossiers distincts pour différents projets ou les types de travail, vous pouviez uniquement les répertoires organisées uniquement par les types de fichiers. Il peut y avoir des dossiers pour les Documents texte, des fichiers PDF, des Images et des feuilles de calcul. Lorsque vous travaillez sur une tâche qui implique plusieurs types de documents, vous devez conserver rebondit entre les différents dossiers et le défilement ou parcourir de nombreux fichiers dans chaque dossier qui ne sont pas liées à la tâche en cours. C’est exactement l’utilisation de fonctionnalités au sein d’une application MVC organisée de la manière par défaut.

Il s’agit d’un problème parce que les groupes de fichiers classés par type, plutôt que d’objectif, ont tendance à un manque de cohésion. Cohésion désigne le degré auquel les éléments d’un module appartiennent ensemble. Dans un projet ASP.NET MVC par défaut, un contrôleur donné fait référence à un ou plusieurs affichages (dans un dossier correspondant pour le nom du contrôleur) associés. Le contrôleur et la vue référence une ou plusieurs ViewModel liés à responsabilité du contrôleur. En règle générale, cependant, quelques types de ViewModel ou des vues sont utilisés par plusieurs types de contrôleur (et, en règle générale, le modèle de domaine ou le modèle de persistance est déplacé vers son propre projet distinct).

Un exemple de projet

Imaginez un projet simple chargé de gérer le quatre concepts vaguement liées application : Araignées géantes, les installations, les Pirates et les Zombies. L’exemple réel vous permet uniquement de répertorier, d’afficher et d’ajouter ces concepts. Toutefois, imaginez une complexité supplémentaire impliquant plusieurs vues. La structure d’organisation par défaut pour ce projet se présenterait comme Figure 2.

Exemple de projet avec l’organisation par défaut
Figure 2 exemple de projet avec l’organisation par défaut

Pour utiliser un nouveau bit de fonctionnalité impliquant des Pirates, vous devez naviguer vers le bas dans les contrôleurs et trouver le PiratesController puis naviguez dans des vues dans les Pirates dans le fichier de vue appropriée. Même avec les contrôleurs que cinq, vous pouvez voir cela représente beaucoup de navigation bat du dossier. Cela est souvent aggravé lorsque la racine du projet inclut le plus grand nombre de dossiers, étant donné que les contrôleurs et les vues ne sont pas près de l’autre par ordre alphabétique (de sorte que les dossiers supplémentaires ont tendance à se situent entre les deux dans la liste des dossiers).

Une autre approche pour organiser les fichiers par leur type consiste à les organiser selon les lignes de l’application. Au lieu de dossiers pour les contrôleurs, modèles et des vues, votre projet aurait dossiers organisés autour de fonctionnalités ou des zones de responsabilité. Lorsque vous travaillez sur un bogue ou une fonctionnalité associée à une fonctionnalité particulière de l’application, vous devez maintenir moins de dossiers parce que les fichiers connexes peuvent être stockés ensemble. Pour cela, dans une de plusieurs façons, notamment à l’aide de la fonctionnalité intégrée de zones et de déploiement de votre propre convention pour les dossiers fonctionnalité.

Le cœur d’ASP.NET MVC voit des fichiers

Mieux vaut passer un moment pour examiner comment ASP.NET MVC de base fonctionne avec les types standards de fichiers utilise une application générée dans celui-ci. La plupart des fichiers impliqués dans le côté serveur de l’application vont être les classes écrites dans un langage .NET. Ces fichiers de code peuvent résider n’importe où sur le disque, tant que peut être compilés et référencés par l’application. En particulier, les fichiers de classe de contrôleur ne pas devoir être stockés dans un dossier particulier. Différents types de classes de modèle (modèle de domaine, modèle de vue, modèle de persistance et ainsi de suite) sont identiques et peuvent facilement résident dans des projets séparés à partir du projet ASP.NET MVC Core. Vous pouvez réorganiser et réorganiser la plupart des fichiers de code dans l’application comme vous le souhaitez.

Toutefois, les vues, sont différents. Les vues sont des fichiers de contenu. Leur emplacement de stockage par rapport aux classes de contrôleur de l’application n’est pas pertinent, mais il est important que MVC sait où les trouver. Zones fournissent la prise en charge intégrée pour localiser les vues dans des emplacements différents de celui du dossier d’affichages par défaut. Vous pouvez également personnaliser le MVC détermine l’emplacement des vues.

Organiser des projets MVC à l’aide de zones

Les zones permettent d’organiser des modules indépendants au sein d’une application ASP.NET MVC. Chaque zone comporte une structure de dossiers qui reproduit les conventions de racine du projet. Par conséquent, votre application MVC aurait les mêmes conventions du dossier racine, et un dossier supplémentaire appelé zones, dans lequel est un dossier pour chaque section de l’application, qui contient des dossiers pour les contrôleurs et vues (et éventuellement des modèles ou ViewModel, si vous le souhaitez).

Zones sont une fonctionnalité puissante qui vous permettre de segmenter une grande application en sous-applications séparées et distinctes logiquement. Contrôleurs, par exemple, peuvent avoir le même nom dans des zones, et en fait, il est courant d’avoir une classe HomeController dans chaque zone dans une application.

Pour ajouter la prise en charge des domaines à un projet ASP.NET MVC Core, vous devez simplement créer un nouveau dossier de niveau racine appelé zones. Dans ce dossier, créez un nouveau dossier pour chaque partie de votre application qu'afin d’organiser au sein d’une zone. Puis, dans ce dossier, ajouter de nouveaux dossiers pour les contrôleurs et les vues.

Vos fichiers de contrôleur doivent donc se trouver dans :

/Areas/[area name]/Controllers/[controller name].cs

Vos contrôleurs doivent avoir un zone attribut informe l’infrastructure qu’ils appartiennent au sein d’une zone particulière :

namespace WithAreas.Areas.Ninjas.Controllers
{
  [Area("Ninjas")]
  public class HomeController : Controller

Les vues doivent se trouve dans :

/Areas/[area name]/Views/[controller name]/[action name].cshtml

Tous les liens que vous deviez les vues qui ont été déplacées dans les zones doivent être mis à jour. Si vous utilisez des aides à la balise, vous pouvez spécifier le nom de zone dans le cadre de l’application d’assistance de balise. Par exemple :

<a asp-area="Ninjas" asp-controller="Home" asp-action="Index">Ninjas</a>

Liens entre les vues dans la même zone peuvent omettre l’attribut d’asp à la zone.

La dernière chose que vous devez faire pour prendre en charge des zones dans votre application est mise à jour les règles de routage par défaut pour l’application dans Startup.cs dans la méthode de configuration :

app.UseMvc(routes =>
{
  // Areas support
  routes.MapRoute(
    name: "areaRoute",
    template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
  routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id?}");
});

Par exemple, l’exemple d’application pour la gestion des divers araignées géantes, les Pirates et autres susceptible d’utiliser les zones pour obtenir la structure d’organisation de projet, comme indiqué dans Figure 3.

Organiser un projet ASP.NET Core des zones
Figure 3 organiser un projet ASP.NET Core des zones

La fonctionnalité de zones fournit une amélioration au fil de la convention par défaut en fournissant des dossiers séparés pour chaque section de l’application logique. Zones sont une fonctionnalité intégrée dans ASP.NET MVC de base, nécessitant une installation minimale. Si vous utilisez pas déjà les, conservez-les à l’esprit comme un moyen simple de regrouper les rubriques connexes de votre application et séparés du reste de l’application.

Toutefois, l’organisation de zones est encore très lourdes du dossier. Vous pouvez le voir dans l’espace vertical requis pour afficher le petit nombre de fichiers dans le dossier des zones. Si vous ne disposez de plusieurs contrôleurs par zone et que vous n’avez pas plusieurs vues par contrôleur, cette surcharge de dossier peut ajouter friction à peu près la même façon que la convention par défaut.

Heureusement, vous pouvez facilement créer votre propre convention.

Dossiers de fonctionnalités de base d’ASP.NET MVC

En dehors de la convention de dossier par défaut ou l’utilisation de la fonctionnalité intégrée de zones, le moyen le plus populaire pour organiser les projets MVC est avec des dossiers par fonctionnalité. Cela est particulièrement vrai pour les équipes qui ont adopté fournissant des fonctionnalités tranches verticales (voir bit.ly/2abpJ7t), car la plupart des problèmes de l’interface utilisateur d’une tranche verticale peut exister dans un de ces dossiers fonctionnalité.

Lors de l’organisation de votre projet par fonctionnalité (plutôt que par type de fichier), vous aurez généralement un dossier racine (par exemple, des fonctionnalités) dans lequel vous avez un sous-dossier par fonctionnalité. Cela est très similaire à l’organisation des domaines. Toutefois, dans chaque dossier de fonctionnalité, vous puissiez inclure tous les contrôleurs requis, les vues et les types de ViewModel. Dans la plupart des applications, il en résulte un dossier avec peut-être des éléments de 5 à 15, qui sont étroitement liés entre eux. Tout le contenu du dossier fonctionnalités peut être conservé dans la vue dans l’Explorateur de solutions. Vous pouvez voir un exemple de cette organisation de l’exemple de projet dans Figure 4.

Organisation de dossier de fonctionnalité
Figure 4 fonctionnalité dossier organisation

Notez que même les contrôleurs de niveau racine et les dossiers des affichages ont été éliminées. La page d’accueil de l’application est désormais dans son propre dossier de fonctionnalité appelée accueil et fichiers partagés comme _Layout.cshtml se trouvent dans un dossier partagé dans le dossier de fonctionnalités. Cette structure d’organisation de projet s’adapte très bien et permet aux développeurs de cibler leurs dossiers beaucoup moins tout en travaillant sur une partie d’une application.

Dans cet exemple, à la différence des zones, des itinéraires supplémentaires sont requis et sans attributs nécessaires pour les contrôleurs (Notez, toutefois, que les noms de contrôleur doivent être uniques entre les fonctionnalités de cette implémentation). Pour prendre en charge cette organisation, vous avez besoin d’un personnalisé IViewLocationExpander et IControllerModelConvention. Ceux-ci sont tous deux utilisés, ainsi que certains ViewLocationFormats personnalisé, configurer MVC dans votre classe de démarrage.

Pour un contrôleur donné, il est utile de savoir quelle fonction elle est associée. Zones de parvenir à l’aide d’attributs ; Cette approche utilise une convention. La convention attend que le contrôleur soit dans un espace de noms appelé « Fonctionnalités » et l’élément suivant dans la hiérarchie de l’espace de noms après « Fonctionnalités » par le nom de fonction. Ce nom est ajouté aux propriétés qui sont disponibles au cours de l’emplacement d’affichage, comme indiqué dans Figure 5.

Figure 5 FeatureConvention : IControllerModelConvention

{
  public void Apply(ControllerModel controller)
  {
    controller.Properties.Add("feature", 
      GetFeatureName(controller.ControllerType));
  }
  private string GetFeatureName(TypeInfo controllerType)
  {
    string[] tokens = controllerType.FullName.Split('.');
    if (!tokens.Any(t => t == "Features")) return "";
    string featureName = tokens
      .SkipWhile(t => !t.Equals("features",
        StringComparison.CurrentCultureIgnoreCase))
      .Skip(1)
      .Take(1)
      .FirstOrDefault();
    return featureName;
  }
}

Vous ajoutez cette convention dans le cadre de le MvcOptions lors de l’ajout de MVC dans le démarrage :

services.AddMvc(o => o.Conventions.Add(new FeatureConvention()));

Pour remplacer la logique d’emplacement en mode normal utilisée par MVC avec la convention de fonctionnalité, vous pouvez effacer la liste des ViewLocationFormats utilisé par MVC et remplacez-le par votre propre liste. Cela fait partie de l’appel AddMvc, comme indiqué dans Figure 6.

Figure 6, en remplaçant la normale visualiser la logique de l’emplacement utilisée par MVC

services.AddMvc(o => o.Conventions.Add(new FeatureConvention()))
  .AddRazorOptions(options =>
  {
    // {0} - Action Name
    // {1} - Controller Name
    // {2} - Area Name
    // {3} - Feature Name
    // Replace normal view location entirely
    options.ViewLocationFormats.Clear();
    options.ViewLocationFormats.Add("/Features/{3}/{1}/{0}.cshtml");
    options.ViewLocationFormats.Add("/Features/{3}/{0}.cshtml");
    options.ViewLocationFormats.Add("/Features/Shared/{0}.cshtml");
    options.ViewLocationExpanders.Add(new FeatureViewLocationExpander());
  }

Par défaut, ces chaînes de format incluent des espaces réservés pour les actions (« {0} »), les contrôleurs (« {{1} ») et les zones (« {{2} »). Cette approche ajoute un quatrième jeton (« {{3} ») pour les fonctionnalités.

Les formats d’emplacement de vue utilisées doivent prendre en charge les vues portant le même nom mais utilisent par différents contrôleurs dans une fonction. Par exemple, il est assez courant d’avoir plusieurs contrôleurs dans une fonction et pour plusieurs contrôleurs de disposer d’une méthode de l’Index. Cela est pris en charge en recherchant des vues dans un dossier correspondant au nom du contrôleur. Par conséquent, NinjasController.Index et SwordsController.Index seraient rechercher les vues /Features/Ninjas/Ninjas/Index.cshtml et /Features/Ninjas/Swords/Index.cshtml, respectivement (voir Figure 7).

Plusieurs contrôleurs par fonctionnalité
Figure 7 plusieurs contrôleurs par fonctionnalité

Notez que cela est facultatif, si vos fonctionnalités n’ont pas besoin de lever l’ambiguïté entre les vues (par exemple, étant donné que la fonction a un seul contrôleur), vous pouvez seulement mettre les vues directement dans le dossier de la fonctionnalité. En outre, si vous préférez utiliser des préfixes de fichier que les dossiers, vous pouvez ajuster facilement la chaîne de format pour utiliser « {3}{1} » au lieu de « {3}/{1}, » résultant dans l’affichage des noms de fichiers tels que NinjasIndex.cshtml et SwordsIndex.cshtml.

Les affichages partagés sont également pris en charge, dans la racine du dossier fonctionnalités et dans un sous-dossier partagé.

L’interface IViewLocationExpander expose une méthode, ExpandViewLocations, qui est utilisées par l’infrastructure pour identifier les dossiers qui contiennent des vues. Ces dossiers sont recherchés lorsqu’une action retourne une vue. Cette approche requiert uniquement le ViewLocationExpander remplacer le jeton « {{3} » avec le nom de la fonctionnalité du contrôleur, spécifié par la FeatureConvention décrite précédemment :

public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context,
  IEnumerable<string> viewLocations)
{
  // Error checking removed for brevity
  var controllerActionDescriptor =
    context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
  string featureName = controllerActionDescriptor.Properties["feature"] as string;
  foreach (var location in viewLocations)
  {
    yield return location.Replace("{3}", featureName);
  }
}

Pour prendre en charge la publication correctement, vous devez également mettre à jour publishOptions de project.json pour inclure le dossier fonctionnalités :

"publishOptions": {
  "include": [
    "wwwroot",
    "Views",
    "Areas/**/*.cshtml",
    "Features/**/*.cshtml",
    "appsettings.json",
    "web.config"
  ]
},

La nouvelle convention de l’utilisation d’un dossier appelé Features est intégralement sous votre contrôle, ainsi que la façon dont les dossiers sont organisés qu’il contient. En modifiant le jeu de ViewLocationFormats (et éventuellement le comportement du type FeatureViewLocationExpander), vous pouvez avoir un contrôle total sur où se trouvent les vues de votre application, qui est la seule chose nécessaire pour réorganiser vos fichiers, car les types de contrôleur sont découverts, quelle que soit le dossier dans lequel ils se trouvent.

Dossiers de la fonctionnalité de côte à côte

Si vous souhaitez essayer la fonctionnalité dossiers de côte à côte avec les conventions de zone MVC et l’affichage par défaut, vous pouvez le faire avec petites modifications uniquement. Au lieu de désactiver le ViewLocationFormats, insérez les formats de fonctionnalité dans le début de la liste (Notez l’ordre est inversé) :

options.ViewLocationFormats.Insert(0, "/Features/Shared/{0}.cshtml");
options.ViewLocationFormats.Insert(0, "/Features/{3}/{0}.cshtml");
options.ViewLocationFormats.Insert(0, "/Features/{3}/{1}/{0}.cshtml");

Pour prendre en charge les fonctionnalités associées à des zones, modifier la collection AreaViewLocationFormats :

options.AreaViewLocationFormats.Insert(0, "/Areas/{2}/Features/Shared/{0}.cshtml");
options.AreaViewLocationFormats.Insert(0, "/Areas/{2}/Features/{3}/{0}.cshtml");
options.AreaViewLocationFormats.Insert(0, "/Areas/{2}/Features/{3}/{1}/{0}.cshtml");

Qu’en est-il des modèles ?

Lecteurs attentifs auront remarqué que Mes types de modèle n’a pas déplacer dans les dossiers de fonctionnalités (ou zones). Dans cet exemple, je n’ai types ViewModel distincts, car les modèles que j’utilise sont incroyablement simples. Dans une application réelle, il est probable que votre domaine ou modèle de persistance aura plus de complexité que nécessitent vos vues, et il allez définie dans son propre projet distinct. Votre application MVC définiront probablement des types de ViewModel qui contiennent uniquement les données requises pour une vue donnée, optimisée pour l’affichage (ou la consommation à partir de la demande d’un client de l’API). Ces types de ViewModel doivent absolument être placés dans le dossier de fonctionnalité où ils sont utilisés (et il est rare pour être partagé entre les fonctionnalités de ces types).

Pour résumer

L’exemple inclut les trois versions de l’application de bibliothèque multimédia NinjaPiratePlantZombie, avec prise en charge pour l’ajout et affichage de chaque type de données. Télécharger (ou afficher à GitHub) et la réflexion sur le fonctionne de chaque approche dans le contexte d’une application, vous travaillez actuellement. Essayez d’ajouter une zone ou un dossier de fonctionnalité à une application plus importante, vous travaillez sur et décidez si vous préférez travailler avec les tranches de la fonction en tant que l’organisation de niveau supérieur de dossier structure plutôt que d’avoir dossiers de niveau supérieur votre application en fonction des types de fichier.

Le code source pour cet exemple est disponible à l’adresse bit.ly/29MxsI0.


Steve Smith est un formateur indépendant, mentor et consultant, ainsi que MVP ASP.NET.  Il a participé à des dizaines d’articles à la documentation officielle ASP.NET (docs.asp.net) et aide les équipes à être rapidement opérationnel avec ASP.NET Core. Contactez-le à l’adresse ardalis.com.


Merci à l'expert technique suivant d'avoir relu cet article : Ryan Nowak
Ryan Nowak est un développeur qui travaille sur l’équipe ASP.NET de Microsoft.