2009-02-21 7 views
0

J'ai donc un modèle de règle de validation simple que j'utilise pour effectuer une validation sur des objets d'entité. Voici ma classe ValidationRule:J'ai des problèmes avec les génériques et la diffusion en C#

public class ValidationRule { 

    public Func<object, bool> Rule { get; set; } 
    public string ErrorMessage { get; set; } 

    public ValidationRule(string errorMessage, Func<object, bool> rule) { 
     Rule = rule; 
     ErrorMessage = errorMessage; 
    } 

    public bool IsValid(object obj) { 
     return Rule(obj); 
    } 
} 

J'ai une classe de base pour mes objets d'entité qui encapsulent les méthodes pour effectuer la validation qui ressemble à ceci:

public abstract class ModelBase { 

    private List<ValidationRule> _validationRules; 
    public List<ValidationRule> ValidationRules { 
     get { 
      if (_validationRules == null) 
       _validationRules = new List<ValidationRule>(); 
      return _validationRules; 
     } 
     set { _validationRules = value; } 
    } 

    public ValidationResult Validate() { 
     var result = new ValidationResult(); 
     rules.ForEach(r => { 
      if (!r.IsValid(this)) 
       result.Errors.Add(
        new ValidationError(r.ErrorMessage, r.PropertyName));    
      }); 
     return result; 
    } 
} 

Et maintenant est là le vrai problème, je suis essayant de résoudre. Lorsque je crée une nouvelle classe qui hérite de ModelBase, l'ajout de règles de validation est un peu gênant. Par exemple:

public class Client : ModelBase { 

    public int ID{ get; set; } 
    public string Name { get; set; } 
    public Address MailingAddress { get; set; } 

    public Client() { 
     CreateValidationRules(); 
    } 

    private void CreateValidationRules() { 

     ValidationRules.Add(new ValidationRule("Client 'Name' is required.", 
      c => !string.IsNullOrEmpty(((Client)c).Name))); 
    } 
} 

Notez où je crée la liste des règles de validation. A l'intérieur de l'expression lambda, je dois lancer "c" en "Client" car ma règle est essentiellement Func<object, bool>. J'ai essayé plusieurs façons de rendre ce générique en faisant quelque chose comme ValidationRule<Client> mais je rencontre toujours des problèmes avec l'appel Validate() dans la classe ModelBase. Des idées sur la façon de contourner ce casting?

+0

comme une note de côté, Func est le même que prédicats bendewey

+0

"le même" type de fonctionnalité. Mais pour une raison étrange, ils ne sont pas les mêmes dans le système de types. – MichaelGG

+0

Ils ne sont pas identiques, mais vous pouvez convertir entre les deux. –

Répondre

4

Vous pouvez rendre la classe ValidationRule générique, mais laissez le paramètre de la méthode IsValid en tant qu'objet et effectuez la conversion dans la méthode. De cette façon, vous obtenez les génériques sans avoir à faire générique ModelBase aussi.

Vous avez également besoin d'une interface pour que ModelBase puisse conserver une liste de règles de validation sans connaître leur type réel. Il suffit ensuite de modifier le type de la liste et la propriété dans ModelBase en IValidationRule.

(Note:. Vous pouvez utiliser un setter privé sur les propriétés pour les rendre en lecture seule)

public Interface IValidationRule { 
    bool IsValid(object); 
} 

public class ValidationRule<T> : IValidationRule { 

    public Func<T, bool> Rule { get; private set; } 
    public string ErrorMessage { get; private set; } 

    public ValidationRule(string errorMessage, Func<object, bool> rule) { 
     Rule = rule; 
     ErrorMessage = errorMessage; 
    } 

    public bool IsValid(object obj) { 
     return Rule((T)obj); 
    } 
} 

Maintenant, le type du paramètre dans l'expression de lamda est le type générique, de sorte que vous don « t doivent jeter:

ValidationRules.Add(
     new ValidationRule<Client>(
      "Client 'Name' is required.", 
      c => !string.IsNullOrEmpty(c.Name) 
    ) 
); 
1

Je suppose que ModelBase doit aussi devenir générique. Ce n'est pas clair pour moi exactement ce qui se passe ici - les règles de validation ne seront-elles pas les mêmes pour tous les Client? Les règles de validation semblent devoir être associées à un type entier, et non à des instances individuelles d'un type. Ils seraient validés contre une seule instance, certes.

Je me demande si vous ne devriez pas avoir un type Validator<T>, et créer (une fois) un Validator<Client> qui peut ensuite être validé par rapport à n'importe quelle instance de Client.

+0

Est-ce que Prédicat est préféré à Func ? – bendewey

+0

Personnellement, je pense que c'est plus clair, oui. Je ne suis pas sûr pourquoi LINQ utilise Func pour des choses comme Où. –

+0

Prédicat pour une surcharge et Func pour l'autre ... nah. http://msdn.microsoft.com/en-us/library/system.linq.enumerable.where.aspx –

Questions connexes