15 May
2010

Zwei Properties beim ViewModel auf Gleichheit prüfen mit ASP.NET MVC 2

 

Es gibt Anlässe bei denen man beim Input ViewModel prüfen muss ob zwei Properties den gleichen Inhalt haben. Ein typischer Fall ist die Kennwort-Eingabe in einem Webformular.

ASP.NET MVC 2 bietet da nichts out of the Box, aber über die DataAnnotations-Validierung kann man sich einfach eigenen Attribute bauen die dies machen können.

Vor einiger Zeit stellte Thomas Bandt eine Lösung vor, diese war mit bei den Property-Namen ein wenig zu tolerant (die Groß- und Kleinschreibung war egal) und es wurde ein eventuell angegebener Display Name nicht in die Fehlermeldung übernommen.

Diese Lösung habe ich verbessert und stelle dies nun auch der Allgemeinheit zu Verfügung.

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

    private readonly string confirmProperty;
    private readonly string originalProperty;
    private ModelMetadata metaData;

    public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)
        : base(DefaultErrorMessage)
    {
        this.originalProperty = originalProperty;
        this.confirmProperty = confirmProperty;
    }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentCulture,
                             ErrorMessageString,
                             GetPropertyDisplayName(originalProperty),
                             GetPropertyDisplayName(confirmProperty)
            );
    }

    private string GetPropertyDisplayName(string propertyName)
    {
        var displayName = metaData.Properties
            .Where(s => s.PropertyName == propertyName)
            .Single().DisplayName;
        return string.IsNullOrEmpty(displayName) ? propertyName : displayName;
    }

    public override bool IsValid(object instance)
    {
        metaData = ModelMetadataProviders.Current.GetMetadataForType(() => instance, instance.GetType());

        return Equals(GetValueFromProperty(instance, originalProperty),
                      GetValueFromProperty(instance, confirmProperty));
    }

    private static object GetValueFromProperty(object value, string propertyName)
    {
        return value.GetType().GetProperty(propertyName).GetValue(value, null);
    }
}

Die Anwendung ist identisch mit der Variante von Thomas. Einfach eine Klasse damit attributieren und gut ist.

[PropertiesMustMatch("Password", "PasswordConfirmation")]
public class UserRegistrationModel
{
    [Required]
    [StringLength(32)]
    public string Username { get; set; }

    [Required]
    [StringLength(256)]
    public string EMail { get; set; }

    [Required]
    public string Password { get; set; }

    [Required]
    public string PasswordConfirmation { get; set; }
}

Die Validierung funktioniert auch mit meiner Methode zum Unit-Testen von Validierungen.

Bei meinem Projekt ist die Klasse natürlich selbst auch komplett mit Unit-Tests abgedeckt, diese könnt Ihr euch im Repository ansehen. Die Tests sind im BDD Style mit den von mir verwendeten Framework xUnit.net mit den xunitbddextensions von Björn Rochel.


Der Eintrag ist mir etwas Wert
 
Comments have been closed on this topic.