web-dev-qa-db-fra.com

Attribut de validation personnalisé qui compare la valeur de ma propriété avec la valeur d'une autre propriété dans ma classe de modèle

Je veux créer un attribut de validation personnalisé, dans lequel je veux comparer la valeur de ma propriété avec la valeur d'une autre propriété dans ma classe de modèle. Par exemple, j'ai dans ma classe de modèle:

...    
public string SourceCity { get; set; }
public string DestinationCity { get; set; }

Et je veux créer un attribut personnalisé pour l'utiliser comme ceci:

[Custom("SourceCity", ErrorMessage = "the source and destination should not be equal")]
public string DestinationCity { get; set; }
//this wil lcompare SourceCity with DestinationCity

Comment puis-je m'y rendre?

53
TheForbidden

Voici comment vous pouvez obtenir l'autre valeur de propriété:

public class CustomAttribute : ValidationAttribute
{
    private readonly string _other;
    public CustomAttribute(string other)
    {
        _other = other;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var property = validationContext.ObjectType.GetProperty(_other);
        if (property == null)
        {
            return new ValidationResult(
                string.Format("Unknown property: {0}", _other)
            );
        }
        var otherValue = property.GetValue(validationContext.ObjectInstance, null);

        // at this stage you have "value" and "otherValue" pointing
        // to the value of the property on which this attribute
        // is applied and the value of the other property respectively
        // => you could do some checks
        if (!object.Equals(value, otherValue))
        {
            // here we are verifying whether the 2 values are equal
            // but you could do any custom validation you like
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
        return null;
    }
}
87
Darin Dimitrov

Veuillez regarder ci-dessous pour mon exemple:

classe modèle implémente INotifyPropertyChanged

public class ModelClass : INotifyPropertyChanged
{
    private string destinationCity;

    public string SourceCity { get; set; }

    public ModelClass()
    {
        PropertyChanged += CustomAttribute.ThrowIfNotEquals;
    }

    [Custom("SourceCity", ErrorMessage = "the source and destination should not be equal")]
    public string DestinationCity
    {
        get
        {
            return this.destinationCity;
        }
        set
        {
            if (value != this.destinationCity)
            {
                this.destinationCity = value;
                NotifyPropertyChanged("DestinationCity");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

classe d'attribut contient également un événement hendler.

internal sealed class CustomAttribute : Attribute
{
    public CustomAttribute(string propertyName)
    {
        PropertyName = propertyName;
    }

    public string PropertyName { get; set; }

    public string ErrorMessage { get; set; }

    public static void ThrowIfNotEquals(object obj, PropertyChangedEventArgs eventArgs)
    {
        Type type = obj.GetType();

        var changedProperty = type.GetProperty(eventArgs.PropertyName);

        var attribute = (CustomAttribute)changedProperty
            .GetCustomAttributes(typeof(CustomAttribute), false)
            .FirstOrDefault();

        var valueToCompare = type.GetProperty(attribute.PropertyName).GetValue(obj, null);

        if (!valueToCompare.Equals(changedProperty.GetValue(obj, null)))
            throw new Exception("the source and destination should not be equal");
    }
}

tilisation

    var test = new ModelClass();
    test.SourceCity = "1";
    // Everything is ok
    test.DestinationCity = "1";
    // throws exception
    test.DestinationCity ="2";

Pour simplifier le code, j'ai décidé d'omettre une validation.

7
user854301

La meilleure façon de le faire est d'utiliser IValidatableObject. Voir http://msdn.Microsoft.com/en-us/data/gg193959.aspx

2
anmaia