Question

J'ai une situation dans laquelle je souhaite comparer des champs (par exemple, en veillant à ce que l'heure de début soit antérieure à l'heure de fin). J'utilise les attributs System.ComponentModel.DataAnnotations pour ma validation.

Ma première pensée a ressemblé à ceci:

public enum CompareToOperation
{
    EqualTo,
    LessThan,
    GreaterThan
}

public class CompareToAttribute : ValidationAttribute
{
    CompareToOperation _Operation;
    IComparable _Comparision;

    public CompareToAttribute(CompareToOperation operation, Func<IComparable> comparison)
    {
       _Operation = operation;
       _Comparision = comparison();
    }

    public override bool IsValid(object value)
    {
    if (!(value is IComparable))
        return false;

    switch (_Operation)
    {
        case CompareToOperation.EqualTo: return _Comparision.Equals(value);
        case CompareToOperation.GreaterThan: return _Comparision.CompareTo(value) == 1;
        case CompareToOperation.LessThan: return _Comparision.CompareTo(value) == -1;
    }

    return false;
    }
}

public class SimpleClass
{
   public DateTime Start {get;set;}
   [CompareTo(CompareToOperation.GreaterThan, () => this.Start)] // error here
   public DateTime End {get;set;}
}

Cela ne fonctionne pas, cependant, il y a une erreur de compilation où l'attribut est marqué:

Expression cannot contain anonymous methods or lambda expressions

Quelqu'un at-il une solution à cela? Ou une approche différente pour valider un champ par rapport à la valeur d’un autre?

Était-ce utile?

La solution

Un très vilain moyen qui n’est pas aussi flexible est de le placer dans la classe et d’utiliser la réflexion. Je n'ai pas testé cela, donc je ne suis pas sûr que ça marche, mais ça compile:)

public enum CompareToOperation
{
    EqualTo,
    LessThan,
    GreaterThan
}

public class CompareToAttribute : ValidationAttribute
{
    CompareToOperation _Operation;
    string _ComparisionPropertyName1;
    string _ComparisionPropertyName2;

    public CompareToAttribute(CompareToOperation operation, string comparisonPropertyName1, string comparisonPropertyName2)
    {
        _Operation = operation;
        _ComparisionPropertyName1 = comparisonPropertyName1;
        _ComparisionPropertyName2 = comparisonPropertyName2;
    }

    private static IComparable GetComparablePropertyValue(object obj, string propertyName)
    {
        if (obj == null) return null;
        var type = obj.GetType();
        var propertyInfo = type.GetProperty(propertyName);
        if (propertyInfo == null) return null;
        return propertyInfo.GetValue(obj, null) as IComparable;
    }

    public override bool IsValid(object value)
    {
        var comp1 = GetComparablePropertyValue(value, _ComparisionPropertyName1);
        var comp2 = GetComparablePropertyValue(value, _ComparisionPropertyName2);

        if (comp1 == null && comp2 == null)
            return true;

        if (comp1 == null || comp2 == null)
            return false;

        var result = comp1.CompareTo(comp2);

        switch (_Operation)
        {
            case CompareToOperation.LessThan: return result == -1;
            case CompareToOperation.EqualTo: return result == 0;
            case CompareToOperation.GreaterThan: return result == 1;
            default: return false;
        }
    }
}

[CompareTo(CompareToOperation.LessThan, "Start", "End")]
public class SimpleClass
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
}

Autres conseils

Cochez la case AccountMOdel dans le projet par défaut de MVC2. Un attribut PropertiesMustMatchAttribute est appliqué à ChangePasswordModel pour valider que NewPassword et ConfirmPassword Match

   [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class PropertiesMustMatchAttribute : ValidationAttribute
{
    private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";

    private readonly object _typeId = new object();

    public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
        : base(_defaultErrorMessage)
    {
        OriginalProperty = originalProperty;
        ConfirmProperty = confirmProperty;
    }

    public string ConfirmProperty
    {
        get;
        private set;
    }

    public string OriginalProperty
    {
        get;
        private set;
    }

    public override object TypeId
    {
        get
        {
            return _typeId;
        }
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
            OriginalProperty, ConfirmProperty);
    }

    public override bool IsValid(object value)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
        object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
        object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
        return Object.Equals(originalValue, confirmValue);
    }
}

À première vue, cela ne peut être fait.

ValidationAttribute est appliqué à une propriété et est donc limité à cette propriété uniquement.

Je suppose que la question n'est pas abstraite et que vous avez un problème réel qui nécessite la présence d'un tel validateur. C'est probablement la zone de texte de répétition du mot de passe? : -)

Dans tous les cas, pour contourner le problème que vous rencontrez, vous devez vous fier au contexte dans lequel vous travaillez. Les formulaires Web ASP.NET l'ont fait avec ControlToCompare et, comme tout est un contrôle et que nous avons nommé des conteneurs, c'est assez facile à comprendre à partir d’une simple chaîne.

Dans ASP.NET MVC, vous pouvez théoriquement faire la même chose, MAIS! Le côté client sera assez facile et naturel - utilisez simplement le #PropertyName et faites votre travail en javascript. Serverside, bien que vous ayez besoin d'accéder à quelque chose d'extérieur à votre classe d'attributs - l'objet Request - et c'est un non non, pour ma part.

En résumé, IL Y A TOUJOURS une raison pour que les choses se passent et, à mon avis, une raison pour laquelle Microsoft n’a pas implémenté ce type de validateur, c’est - c’est impossible sans les choses décrites ci-dessus.

MAIS! J'espère vraiment que je me trompe. J'ai besoin de la validation de comparaison pour être facile à utiliser ...

Je pense que vous avez besoin de quelque chose comme ça:

public class EqualsAttribute : ValidationAttribute
{
 private readonly String _To;

 public EqualsAttribute(String to)
 {
  if (String.IsNullOrEmpty(to))
  {
   throw new ArgumentNullException("to");
  }
  if (String.IsNullOrEmpty(key))
  {
   throw new ArgumentNullException("key");
  }
  _To = to;
 }


 protected override Boolean IsValid(Object value, ValidationContext validationContext, out ValidationResult validationResult)
 {
  validationResult = null;
  var isValid = IsValid(value, validationContext);
  if (!isValid)
  {
   validationResult = new ValidationResult(
    FormatErrorMessage(validationContext.DisplayName),
    new [] { validationContext.MemberName });
  }
  return isValid;
 }

 private Boolean IsValid(Object value, ValidationContext validationContext)
 {
  var propertyInfo = validationContext.ObjectType.GetProperty(_To);
  if (propertyInfo == null)
  {
   return false;
  }
  var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
  return Equals(value, propertyValue);
 }

 public override Boolean IsValid(Object value)
 {
  throw new NotSupportedException();
 }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top