2009-09-02 11 views
1

J'utilise xVal dans mon application ASP.NET MVC, ce qui est génial en général. Suite à Steve Sanderson's blog post, j'ai créé un DataAnnotationsValidationRunner pour faire la validation côté serveur des objets attribués. Cela fonctionne très bien pour une classe simple. par exemple. Personne:xVal Comment valider les propriétés enfants de types complexes?

public static class DataAnnotationsValidationRunner 
{ 
    public static IEnumerable<ErrorInfo> GetErrors(object o) 
    { 
     return from prop in TypeDescriptor.GetProperties(o).Cast<PropertyDescriptor>() 
       from attribute in prop.Attributes.OfType<ValidationAttribute>() 
       where !attribute.IsValid(prop.GetValue(o)) 
       select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), o); 
    } 
} 

public class Person 
{ 
    [Required(ErrorMessage="Please enter your first name")] 
    public string FirstName { get; set; } 

    [Required(ErrorMessage = "Please enter your last name")] 
    public string LastName { get; set; } 
} 

Cependant, si j'ajoute une propriété d'adresse à cette personne, et marquer la classe d'adresses avec les attributs DataAnnotation, ils ne seront pas validés. par exemple.

public class Person 
{ 
    [Required(ErrorMessage="Please enter your first name")] 
    public string FirstName { get; set; } 

    [Required(ErrorMessage = "Please enter your last name")] 
    public string LastName { get; set; } 

    public Address Address { get; set; } 
} 

public class Address 
{ 
    [Required(ErrorMessage="Please enter a street address")] 
    public string Street { get; set; } 

    public string StreetLine2 { get; set; } 

    [Required(ErrorMessage = "Please enter your city")] 
    public string City { get; set; } 

    [Required(ErrorMessage = "Please enter your state")] 
    public string State { get; set; } 

    [Required(ErrorMessage = "Please enter your zip code")] 
    public string Zip { get; set; } 

    public string Country { get; set; } 
} 

Un problème est que DataAnnotationValidationRunner ne parcourt pas les propriétés enfants complexes. En outre, si ces erreurs sont ajoutées à une collection d'erreurs, elles doivent toujours être préfixées correctement lorsqu'elles sont ajoutées à l'état du modèle. Par exemple. Les erreurs de personne sont ajoutées comme ceci:

catch (RulesException ex) 
    { 
     ex.AddModelStateErrors(ModelState, "person"); 
    } 

Je pense que les règles d'exceptions Adresse devraient être préfixé par « Person.Address ». Existe-t-il une manière prise en charge de gérer la validation des objets enfants avec xVal, ou la création d'un objet de transfert de données aplati serait-elle la seule solution?

Répondre

2

Tout d'abord, vous devez différer entre le DataAnnotationsModelBinder de Steve Sanderson et

En ce qui concerne votre première question (« DataAnnotationValidationRunner ne marche pas influer sur les propriétés de l'enfant complexes »):

Êtes-vous peut-être se référant à DataAnnotationModelBinder de Brad Wilson? Si c'est le cas, cela devrait vraiment valider les ViewModels complexes jusqu'aux dernières propriétés. Sinon, essayez d'utiliser cela à la place du DataAnnoationsModelRunner que vous utilisez. Cet article de blog blog article sur Client-Side Validation with xVal montre comment.

La première version de DataAnnotationModelBinder comportait un bogue qui provoquait un blocage lors de l'utilisation de viewmodels complexes. Peut-être existe-t-il une nouvelle version qui corrige le crash mais ignore les modèles complexes?

Dans tous les cas, je suggère d'essayer la version de DataAnnotationModelBinder utilisée dans le projet de démonstration de l'article de blog ci-dessus. Je l'utilise dans mon propre projet dans le monde réel et cela fonctionne sur des modèles de vues complexes.

En ce qui concerne votre deuxième question: « Est-il possible prise en charge de la manipulation de validation de l'objet enfant xVal »:

Vous avez pas posté de code résidant sur les formulaires ASPX, mais vous pourriez aussi faire allusion au fait qu'un <% = Html.ClientSideValidation()%> ajoute uniquement la validation côté client aux propriétés immédiates de ce type de modèle, mais pas aux propriétés des objets enfants. Vous pouvez simplement contourner le problème en utilisant plusieurs instructions ClientSideValidation, par exemple:

<%= Html.ClientSideValidation<ModelType>()%> 
<%= Html.ClientSideValidation<ChildModelType>("ChildModelPropertyName")%> 
+0

La deuxième réponse fonctionne mais se décompose si vous avez plusieurs propriétés sur l'objet parent qui associent toutes le même type enfant, mais avec des propriétés différentes. c'est à dire. Utilisateur BillingAddress ShippingAddress Il semble que vous puissiez modifier l'utilitaire de validation xval.jquery pour résoudre ce problème. –

1

J'ai eu le même problème. J'avais besoin de valider des objets complexes qui peuvent apparaître comme une propriété d'un autre objet. Je ne suis pas encore entré (encore) dans la validation côté client, mais l'idée d'Adrian Grigore à propos de html.ClientSideValidation() semble être le bon. J'ai fini par créer une interface de marqueur qui marque toutes les classes que j'ai besoin de valider. Cela pourrait être un attribut ou vous pourriez utiliser cette idée pour toutes les propriétés d'une classe.Fondamentalement, il valide l'objet à l'aide du DataAnnotationsValidationRunner mentionné ci-dessus, puis itère sur les propriétés de l'objet et exécute DataAnnotationsValicationRunner sur tous ces éléments jusqu'à ce qu'il n'y ait plus rien à vérifier.

est ici code pseudo pour ce que je faisais:

IEnumarable<ValidationError> GetErrors(object instance) { 
    List<ValidationError> errors = new List<ValidationError>(); 
    errors.AddRange(GetDataAnnotationErrors(instance)); 
    errors.AddRange(GetPropertyErrors(instance)); 
    return errors; 
} 
IEnumerable<ValidationError> GetDataAnnotationErrors(object instance) { 
    // code very similar to what you have above 
} 
IEnumearable<ValidationError> GetPropertyErrors(object instance) 
{ 
    var errors = new List<ValidationError>(); 
    var objectsToValidate = instance.GetType().GetProperties().Where(p => p.PropertyType.GetInterface().Contains(typeof(IMarkerInterface))); 
    // the call above could do any type of reflecting over the properties you want 
    // could just check to make sure it isn't a base type so that all custom 
    // object would be checked 
    if(objectsToValidate == null) return errors; 
    foreach(object obj in objectsToValidate) 
    { 
      errors.AddRange(GetDataAnnotationErrors(obj)); 
      errors.AddRange(GetPropertyErrors(obj)); 
    } 
    return errors; 
} 

J'espère que cela est clair. J'ai testé ce système sur des objets de domaine et jusqu'ici tout va bien. Travailler quelques plis ici et là, mais l'idée a prouvé son pour ce que je fais.