Question

Depuis cette question, il semble logique qu'un contrôleur crée un Modèle de vue cela reflète plus précisément le modèle que la vue tente d'afficher, mais je suis curieux de connaître certaines conventions (je suis nouveau dans le modèle MVC, si ce n'était pas déjà évident).

En gros, je me posais les questions suivantes :

  1. J'aime normalement avoir une classe/un fichier.Est-ce que cela a du sens avec un Modèle de vue s'il est uniquement créé pour transmettre des données d'un contrôleur à une vue ?
  2. Si un Modèle de vue appartient à son propre fichier et vous utilisez une structure répertoire/projet pour garder les choses séparées, où se trouve le Modèle de vue le fichier appartient-il ?Dans le Contrôleurs annuaire?

C'est essentiellement tout pour l'instant.J'ai peut-être encore quelques questions à poser, mais cela me dérange depuis environ une heure et j'arrive à trouver des conseils cohérents ailleurs.

MODIFIER:En regardant l'échantillon Application NerdDinner sur CodePlex, il semble que les ViewModels fassent partie du Contrôleurs, mais cela me met toujours mal à l'aise qu'ils ne soient pas dans leurs propres fichiers.

Était-ce utile?

La solution

Je crée ce que j'appelle un " ViewModel " pour chaque vue. Je les mets dans un dossier appelé ViewModels dans mon projet Web MVC. Je les nomme d'après le contrôleur et l'action (ou la vue) qu'ils représentent. Par conséquent, si je dois transmettre des données à la vue SignUp du contrôleur d'appartenance, je crée une classe MembershipSignUpViewModel.cs et la place dans le dossier ViewModels.

J'ajoute ensuite les propriétés et méthodes nécessaires pour faciliter le transfert des données du contrôleur à la vue. J'utilise Automapper pour passer de ViewModel au modèle de domaine et inversement si nécessaire.

Cela fonctionne également bien pour les ViewModels composites contenant des propriétés du type des autres ViewModels. Par exemple, si vous avez 5 widgets sur la page d'index du contrôleur d'appartenance et que vous avez créé un ViewModel pour chaque vue partielle, comment passez-vous les données de l'action Index aux partiels? Vous ajoutez une propriété au type MembershipIndexViewModel de type MyPartialViewModel et lors du rendu du partiel, vous transmettez Model.MyPartialViewModel.

Cette façon de procéder vous permet de régler les propriétés partielles de ViewModel sans avoir à modifier la vue Index. Il ne fait toujours que passer dans Model.MyPartialViewModel, de sorte qu'il est moins probable que vous ayez à passer par toute la chaîne des partiels pour réparer quelque chose lorsque vous ne faites qu'ajouter une propriété au ViewModel partiel.

Je vais également ajouter l'espace de noms & "MonProjet.Web.ViewModels &"; web.config afin de me permettre de les référencer dans n'importe quelle vue sans jamais ajouter une déclaration d'importation explicite sur chaque vue. Juste rend un peu plus propre.

Autres conseils

La séparation des classes par catégorie (contrôleurs, modèles de vue, filtres, etc.) n'a aucun sens.

Si vous souhaitez écrire du code pour la section Accueil de votre site Web (/), créez un dossier nommé Accueil et y placez HomeController, IndexViewModel, AboutViewModel, etc., ainsi que toutes les classes associées utilisées par les actions Home.

Si vous avez des classes partagées, comme un ApplicationController, vous pouvez le placer à la racine de votre projet.

Pourquoi séparer les éléments liés (HomeController, IndexViewModel) et conserver ensemble les éléments sans relation entre eux (HomeController, AccountController)?

J'ai écrit un article de blog sur ce sujet.

Je conserve mes classes d'application dans un sous-dossier appelé "Core" (ou une bibliothèque de classes séparée) et j'utilise les mêmes méthodes que KIGG exemple d'application mais avec quelques légères modifications pour rendre mes applications plus SÈCHES.

Je crée une classe BaseViewData dans /Core/ViewData/ où je stocke les propriétés communes à l'ensemble du site.

Après cela, je crée également toutes mes classes de vue ViewData dans le même dossier qui dérivent ensuite de BaseViewData et ont des propriétés spécifiques à la vue.

Ensuite, je crée un ApplicationController dont dérivent tous mes contrôleurs.ApplicationController possède une méthode GetViewData générique comme suit :

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

Enfin, dans mon action Controller, je fais ce qui suit pour construire mon modèle ViewData

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

Je pense que cela fonctionne très bien et permet de garder vos vues bien rangées et vos contrôleurs maigres.

Une classe ViewModel est là pour encapsuler plusieurs éléments de données représentés par des instances de classes dans un objet facile à gérer que vous pouvez transmettre à votre vue.

Il serait judicieux d’avoir vos classes ViewModel dans leurs propres fichiers, dans leur propre répertoire. Dans mes projets, j'ai un sous-dossier du dossier Modèles appelé ViewModels. C’est là que résident mes ViewModels (par exemple, ProductViewModel.cs).

Il n’existe aucun lieu approprié pour conserver vos modèles. Vous pouvez les conserver dans un assemblage séparé si le projet est volumineux et qu’il existe de nombreux ViewModels (objets de transfert de données). Vous pouvez également les conserver dans un dossier séparé du projet de site. Par exemple, dans Oxite , ils sont placés dans le projet Oxite, qui contient également de nombreuses classes. Les contrôleurs dans Oxite sont déplacés vers un projet séparé et les vues sont également dans un projet séparé.
Dans CodeCampServer , les modèles de vue sont nommés * Form et placés dans le projet d'interface utilisateur dans le dossier Modèles.
Dans le projet MvcPress , ils sont placés dans le projet Data, qui contient également tout le code permettant de travailler avec la base de données et un peu plus. (mais je n'ai pas recommandé cette approche, c'est juste pour un échantillon)

Donc vous pouvez voir qu'il y a beaucoup de points de vue. Je conserve généralement mes objets ViewModels (objets DTO) dans le projet de site. Mais lorsque j'ai plus de 10 modèles, je préfère les déplacer vers un assemblage séparé. Habituellement, dans ce cas, je déplace les contrôleurs vers un assemblage séparé également.
Une autre question est de savoir comment mapper facilement toutes les données du modèle à votre ViewModel. Je suggère de consulter la bibliothèque AutoMapper . J'aime beaucoup ça fait tout le sale boulot pour moi.
Et je suggère également de consulter le projet SharpArchitecture . Il fournit une très bonne architecture pour les projets et contient beaucoup de cadres et de guides sympas et une excellente communauté.

voici un extrait de code tiré de mes meilleures pratiques:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

Nous plaçons tous nos modèles de vue dans le dossier Modèles (toute notre logique métier se trouve dans un projet ServiceLayer distinct)

Personnellement, je suggérerais que si ViewModel soit tout sauf trivial, utilisez une classe séparée.

Si vous avez plus d'un modèle de vue, nous suggérons qu'il soit logique de le partitionner dans au moins un répertoire. Si le modèle de vue est partagé ultérieurement, l'espace de noms impliqué dans l'annuaire facilite le déplacement vers un nouvel assemblage.

Dans notre cas, nous avons les modèles avec les contrôleurs dans un projet distinct des vues.

En règle générale, nous avons essayé de déplacer et d'éviter la plupart des éléments ViewData [& "; ... &";] dans ViewModel, nous évitons ainsi les castings et les chaînes magiques, qui sont: une bonne chose.

Le ViewModel contient également certaines propriétés communes telles que les informations de pagination pour les listes ou les informations d’en-tête de la page pour dessiner des chapelures et des titres. À ce moment, la classe de base contient trop d'informations à mon avis et nous pouvons les diviser en trois parties, les informations les plus élémentaires et les plus nécessaires pour 99% des pages d'un modèle de vue de base, puis un modèle pour les listes et un modèle. pour les formulaires contenant des données spécifiques pour ces scénarios et héritant de celui de base.

Enfin, nous implémentons un modèle de vue pour chaque entité afin de traiter les informations spécifiques.

code dans le contrôleur:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

code dans le modèle de vue:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

projets:

  • DevJet.Web (le site Web ASP.NET MVC projet)

  • DevJet.Web.App.Dictionary (un projet de bibliothèque de classes séparée)

    Dans ce projet, j'ai créé des dossiers tels que: DAL, BLL, BO, VM (dossier pour les modèles de vue)

Créez une classe de base de modèle de vue contenant les propriétés généralement requises, telles que le résultat de l'opération et les données contextuelles. Vous pouvez également saisir les données et les rôles de l'utilisateur actuel

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

Dans la classe du contrôleur de base ont une méthode comme PopulateViewModelBase (), cette méthode remplira les données contextuelles et les rôles d’utilisateur. HasError et ErrorMessage, définissez ces propriétés s'il existe une exception lors de l'extraction de données du service / db. Liez ces propriétés à l'affichage pour afficher une erreur. Les rôles d'utilisateur peuvent être utilisés pour afficher la section masquée sur la vue en fonction des rôles.

Pour renseigner les modèles de vue dans différentes actions get, vous pouvez le rendre cohérent en utilisant un contrôleur de base avec la méthode abstraite FillModel

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

Dans les contrôleurs

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top