2010-11-11 25 views
15

Nous essayons de comprendre la validation dans le mvvm faire la validation dans la logique métier ou le modèle. J'ai mis en place la validation par type d'exception dans notre logique métier - un schéma simplifié se trouve ici: alt textMVVM - Validation

Si nous avons beaucoup de d'entrées indépendantes les unes des autres, il n'y a pas de problème, la exception est levée, la zone de texte l'attrape une marque, elle est bordée de rouge pour chaque mauvaise entrée. Cependant, quand nous avons des valeurs dépendantes, nous avons des problèmes. par exemple.

  • Valeur1 et Valeur2 dans le modèle ne doit pas être le même, donc nous avons une fonction de validation dans chacun de ceux qui recherchent la valeur equals et lancer une exception si cela se produit

  • maintenant, si nous définissons Value1 sur 0 et Value2 sur 1, tout va bien

  • La valeur 1 est définie dans l'interface graphique à 1 -> celle-ci est marquée en rouge, car la validation des autres valeurs n'est pas déclenchée, donc Value2 dans le L'interface graphique n'est pas marquée défectueuse.

  • Valeur2 se prépare à 2 dans l'interface graphique, maintenant que nous avons atteint un état valide, mais seulement Valeur2 obtient validé, donc Valeur1 est encore marqué comme défectueux

Y at-il un modèle commun pour résoudre ce problème? nous ne voulons pas introduire de dépendance dans l'interface graphique entre les deux zones de texte, car cette logique ne doit être présente que dans la couche de logique applicative.

Au lieu de mettre en œuvre la validation par exception on pourrait aussi mettre en œuvre l'interface IDataErrorInfo, mais le problème existe toujours, il n'y a aucun moyen de forcer les valeurs en fonction de valider leurs valeurs à nouveau, au moins pas que je peux voir :)

Toute aide est appréciée

acclamations, Manni


[nettoyage, retiré étape unecessary]


15.11.2010 - Part2

ok, grand repensées ici, nous allons avec le niveau BusinessLogic. voici notre configuration actuelle prévue: alt text (l'image est un peu petite échelle ici, s'il vous plaît l'ouvrir sur une fenêtre séparée pour l'afficher en taille réelle) tout est plus ou moins clair, sauf comment notifier tous les viewmodels/modélisez les clones des différents éditeurs si le modèle de données sous la logique métier est modifié. une façon de le faire est de suivre les modèles clonés dans la logique métier qui les crée. Lorsque le modèle de données est modifié à l'aide de la logique métier commit(), tous les autres clones de modèle enregistrés peuvent être avertis des modifications et les propager davantage. alternativement, la logique métier pourrait publier un événement auquel tous les viewmodels s'abonnent afin qu'ils obtiennent également les changements - quelqu'un pourrait-il me donner un indice de ce qui est le mieux?

Merci encore pour l'aide, désolé, je suis si l'esprit bloqué;)

+0

Pourquoi les valeurs de propriété de définition de machine virtuelle dans la couche de gestion? Cela semble être la cause profonde de certains de vos problèmes. Tout le monde (semble-t-il) a une interprétation légèrement différente de ce que signifie MVVM, mais à mon humble avis le modèle est une combinaison d'un modèle de données et d'un contrôleur, donc il détient un sur-ensemble des données dans la machine virtuelle. . Ainsi, votre couche de gestion existante doit être soit incorporée dans le modèle actuel, soit éventuellement déplacée après le modèle (c'est-à-dire de l'autre côté de la limite de la WCF). – slugster

+0

Nous envisageons de fusionner le modèle (qui est notre modèle de données) et BusinessLogic en une couche, c'est-à-dire essentiellement ce que vous voulez dire si je l'ai bien compris. mais je pense que le VM ne devrait pas contenir la validation. Mais toujours le problème de passage de chaîne continue. Lorsque vous déplacez la logique métier de l'autre côté d'une frontière wcf, cela implique-t-il que tout le modèle est simplement un donneur de données muet qui peut être édité et soumis dans son ensemble à la logique métier et évalué là? avez-vous des liens concernant l'utilisation d'une limite wcf et le splitup? merci pour l'aide – manni

+0

Absolument vous faites la validation dans la VM, mais c'est une simple validation, comme * mot de passe est plus long que 6 caractères * ou * adresse e-mail est un format valide *. Vous souhaiterez peut-être découpler votre logique métier du modèle, pensez à utiliser une approche à n niveaux pour le référentiel de données model-> business logic-> (il est courant que le BL et la couche de données soient incorporés dans un service Web, mais il ne doit pas être si vous utilisez simplement une base de données locale). Avoir le BL dans le modèle est toujours une meilleure option que ce que vous avez actuellement. – slugster

Répondre

15

Vous pouvez envisager d'utiliser l'interface System.ComponentModel.IDataErrorInfo.Cette interface très pratique vous donne la possibilité de:

  • faire la validation d'une manière conforme à MVVM
  • faire une validation personnalisée pour un domaine particulier (la validation peut vérifier plusieurs valeurs si vous le souhaitez)
  • bind votre interface utilisateur aux erreurs de validation

Vous implémentez IDataErrorInfo sur votre viewmodel (ou même virtuellement dans votre base de modèle de vue et remplacez-le dans vos modèles de vue dérivés). En raison de la nature de la liaison de données, les valeurs que je dois vérifier sont toutes présentes dans le modèle de vue, et je peux tester n'importe quelle combinaison d'entre elles. Bien sûr, vous avez toujours votre validation dans votre couche de gestion, mais vous n'avez plus besoin de faire un tour à votre couche de gestion (ou modèle) juste pour effectuer une validation.

Voici un exemple rapide à partir d'un écran (WPF) qui rassemble des informations utilisateur et fait la validation de base sur les:

code C#:

#region IDataErrorInfo Members 

    /// <summary> 
    /// Gets an error message indicating what is wrong with this object. 
    /// </summary> 
    /// <value></value> 
    /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns> 
    public override string Error 
    { 
     get 
     { 
      return this["UserCode"] + this["UserName"] + this["Password"] + this["ConfirmedPassword"] + this["EmailAddress"]; 
     } 
    } 

    /// <summary> 
    /// Gets the <see cref="System.String"/> with the specified column name. 
    /// </summary> 
    /// <value></value> 
    public override string this[string columnName] 
    { 
     get 
     { 
      switch (columnName) 
      { 
       case "UserCode": 
        if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 20) 
         return "User Code must be less than or equal to 20 characters"; 
        break; 

       case "UserName": 
        if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 60) 
         return "User Name must be less than or equal to 60 characters"; 
        break; 

       case "Password": 
        if (!string.IsNullOrEmpty(Password) && Password.Length > 60) 
         return "Password must be less than or equal to 60 characters"; 
        break; 

       case "ConfirmedPassword": 
        if (Password != ConfirmedPassword) 
         return Properties.Resources.ErrorMessage_Password_ConfirmedPasswordDoesntMatch; 
        break; 

       case "EmailAddress": 
        if (!string.IsNullOrEmpty(EmailAddress)) 
        { 
         var r = new Regex(_emailRegex); 
         if (!r.IsMatch(EmailAddress)) 
          return Properties.Resources.ErrorMessage_Email_InvalidEmailFormat; 
        } 
        break; 
      } 
      return string.Empty; 
     } 
    } 

    #endregion 

et est le balisage XAML ici pour deux les zones de texte sur la page (note en particulier les propriétés ValidatesOnDataErrors et ValidatesOnExceptions dans la Text de liaison):

<TextBox Name="UserCodeTextBox" 
     Text="{Binding UserCode, 
       Mode=TwoWay, 
       UpdateSourceTrigger=PropertyChanged, 
       ValidatesOnDataErrors=True, 
       ValidatesOnExceptions=True, 
       NotifyOnSourceUpdated=True, 
       NotifyOnTargetUpdated=True}" 
     GotFocus="Input_GotFocus" 
     VerticalAlignment="Top" 
     Margin="165,0,150,0" 
     CharacterCasing="Upper" 
     /> 

<TextBox Name="UserNameTextBox" 
     Text="{Binding UserName, 
       Mode=TwoWay, 
       UpdateSourceTrigger=PropertyChanged, 
       ValidatesOnDataErrors=True, 
       ValidatesOnExceptions=True, 
       NotifyOnSourceUpdated=True, 
       NotifyOnTargetUpdated=True}" 
     GotFocus="Input_GotFocus" 
     VerticalAlignment="Top" 
     Margin="165,30,0,0" 
     /> 
+0

merci pour la réponse détaillée. il est tout à fait différente de l'approche d'exception, car avec l'interface IDataErrorInfo vous essentiellement - définir la valeur - vérifier si l'objet est dans un état valide cela signifie que inbetween vous obtenez un objet avec un état non valide cette est bien si vous avez un bouton commit/save et que vous travaillez sur un clone du modèle, mais que vous allez directement au modèle avec un état invalide est un peu dangereux – manni

+0

@user Les données n'atteindront jamais le modèle de vue dans cet exemple si la validation échoue La liaison va d'abord valider, puis rejeter les données avant qu'elles ne se lient au modèle de vue. –

+1

@josh: puisque la ligne cette méthode [chaîne] prend le nom de propriété et accède à son propre membre avec ce nom, ce membre doit avoir la nouvelle valeur invalide, sinon ce contrôle ne peut pas être fait (et serait fait à l'ancien valeur) - ou ai-je manqué quelque chose ici? – manni

0

Existe-t-il un modèle commun pour résoudre ce problème? nous ne voulons pas introduire de dépendance dans l'interface graphique entre les deux zones de texte, car cette logique ne doit être présente que dans la couche de logique applicative.

  1. Value1 et Value2 sont interdépendants en raison de la condition « Valeur1 et Valeur2 dans le modèle ne doit pas être le même ».

  2. Cela signifie que lorsque change, Value1 change également, et vice versa! En fait, lorsque les changements Value2, le résultat de la validation Value1 change, mais cela est proche de l'instruction précédente.

  3. Value1 et Value2 setters doivent informer à la fois Value1 et Value2 changement de propriété.

  4. La vue doit relire et revalider les deux valeurs et effacer la marque défectueuse.

  5. Vous ne savez pas si WPF le fera s'il trouve que l'événement de notification a été déclenché mais que la valeur n'a pas changé.