2010-12-08 8 views
5

Disons que j'ai deux vues dans mon application, MemberListView et MemberEditView. Ils sont associés à leur perspective viewModels, MemberListViewModel et MemberEditViewModel. Les modèles parlent à une classe de référentiel, MemberRepository, qui possède les méthodes CRUD pour la classe membre.MVVM and Repository Question

Dans le formulaire MemberEditView, j'ai plusieurs listes déroulantes qui affichent des thinkgs comme Statut (Actif/Inactif/En attente), le code de commerce des membres etc. Ce sont des objets ObservableCollection dans mon viewModel et sont liés aux ComboBoxes sur la vue. Le MemberRepository doit-il gérer les récupérations pour récupérer les listes de chacun à afficher?

Que faire si sur MemberEditView J'ai une grille qui affiche tous les emplois que le membre a eu au cours des années. Si l'utilisateur double-clique sur l'un des travaux, il appelle JobHistoryEditView pour afficher le travail Information et il a un JobHistoryViewModel. Le MemberRepository doit-il prendre en charge les méthodes CRUD JobHistory ou dois-je avoir un référentiel JobHistory distinct?

Répondre

2

La plupart des applications MVVM auraient cette architecture:

View -> ViewModel -> Model -> Repository 

J'ai récemment épousent une variante:

View -> ViewModel <- Presenter -> Model -> Repository 

(Où A -> B signifie "A connaît B", mais B ne sait pas sur A.)

Notez que dans les deux cas, la seule chose qui connaît le référentiel est le modèle, pas le ViewModel. Votre modèle n'est pas seulement les entités du domaine, il doit également héberger la logique métier. Il est évident que l'une des histoires d'utilisateur de votre logique métier doit prendre en charge est quelque chose que je vais appeler un MemberEditTask:

public class MemberEditTask 
{ 
    private readonly Member _member; 

    public MemberEditTask(Member member, IRepository repository) 
    { 
     this._member = member; 
     this.StatusChoices = repository.GetPossibleMemberStatuses(member); 
    } 

    public ReadOnlyCollection<MemberStatus> StatusChoices { get; private set; } 

    public MemberStatus Status 
    { 
     get { return this._member.Status; } 
     set 
     { 
      if(!this.StatusChoices.Contains(value)) 
      { 
       throw new ArgumentOutOfRangeException(); 
      } 
      this._member.Status = value; 
     } 
    } 
} 

Toute cette logique appartient dans votre modèle, car la liste des choix possibles (et de valider que l'un d'entre eux était effectivement choisi) est défini par la logique métier. Vous pouvez également imaginer une autre chose consommant le MemberEditTask, comme un processus automatisé s'exécutant sur le serveur qui édite un membre en réponse à un fichier téléchargé sur un serveur FTP, ou un processus en arrière-plan (mettre le statut à Inactif après un certain temps). Toutes ces choses doivent exécuter les mêmes règles métier, donc tout doit être commun (pas dans le ViewModel).

Donc, étant donné que la classe, la classe ViewModel ressemble à ceci:

public class MemberEditViewModel : ViewModelBase 
{ 
    private readonly MemberEditTask _task; 

    public MemberEditViewModel(MemberEditTask task) 
    { 
     this._task = task; 
    } 

    public IEnumerable<MemberStatus> StatusChoices 
     { get { return this._task.StatusChoices; } 

    public MemberStatus Status 
    { 
     get { return this._task.Status; } 
     set 
     { 
      this._task.Status = value; 
      NotifyAllPropertiesChanged(); 
     } 
    } 
} 

Dans ce cas, comme une commodité très simple, croient simplement que NotifyAllPropertiesChanged est une méthode de ViewModelBase protégée qui utilise la réflexion pour élever un PropertyChanged événement sur tous propriétés publiques du ViewModel. :) C'est exagéré bien sûr, mais il conduit à un point plus important ...

Ceci est presque un exemple idiot, car dans ce cas, MemberEditViewModel est inutile. Si la vue est le seul paramètre Status alors il n'y a vraiment pas besoin d'augmenter l'événement changé de propriété non plus! Bien sûr, dans le monde réel, vous aurez plus de propriétés et il y aura des interactions. La raison pour laquelle ViewModel existe est d'informer les consommateurs lorsque leurs propriétés liées à la vue changent, ce que le modèle ne fait pas (et ne devrait pas selon moi). (Le ViewModel dispose également d'une logique supplémentaire spécifique à la vue pour prendre en charge les animations, etc.)

Retour à votre question ...La question de savoir si le MemberRepository est responsable de l'exécution des get des statuts n'est pas pertinente du point de vue du ViewModel, car le référentiel est un service utilisé par le Model. Le modèle est un service utilisé par ViewModel. Faites votre modèle de la tâche/flux de travail/processus/tout ce qui expose la liste des options de statut.

Désolé si c'était long.