Created
December 11, 2018 12:03
-
-
Save vanillajonathan/17317a2d7dfe6ecd809221a4feec2d6a to your computer and use it in GitHub Desktop.
Data annotation validation attribute that compares one property with another.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel.DataAnnotations; | |
using System.Globalization; | |
using System.Linq; | |
using System.Reflection; | |
namespace Example | |
{ | |
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] | |
public class CompareToAttribute : ValidationAttribute | |
{ | |
public static string FormatEqual = "{0} must equal {1}."; | |
public static string FormatLessThan = "{0} must be less than {1}."; | |
public static string FormatLessThanOrEqual = "{0} must be less than or equal to {1}."; | |
public static string FormatGreaterThan = "{0} must be greater than {1}."; | |
public static string FormatGreaterThanOrEqual = "{0} must be greater than or equal to {1}."; | |
public static string FormatNotEqual = "{0} must not equal {1}."; | |
private readonly Comparison _comparison; | |
public CompareToAttribute(Comparison comparison, string otherProperty) : base("SR.CompareAttribute_MustMatch") | |
{ | |
OtherProperty = otherProperty ?? throw new ArgumentNullException(nameof(otherProperty)); | |
_comparison = comparison; | |
} | |
public string OtherProperty { get; } | |
public string OtherPropertyDisplayName { get; internal set; } | |
public override bool RequiresValidationContext => true; | |
public override string FormatErrorMessage(string name) => | |
string.Format( | |
CultureInfo.CurrentCulture, ErrorMessageString, name, OtherPropertyDisplayName ?? OtherProperty); | |
protected override ValidationResult IsValid(object value, ValidationContext validationContext) | |
{ | |
var otherPropertyInfo = validationContext.ObjectType.GetRuntimeProperty(OtherProperty); | |
if (otherPropertyInfo == null) | |
{ | |
return new ValidationResult($"CompareAttribute_UnknownProperty {OtherProperty}"); | |
} | |
if (otherPropertyInfo.GetIndexParameters().Any()) | |
{ | |
throw new ArgumentException($"Common_PropertyNotFound {validationContext.ObjectType.FullName} {OtherProperty}"); | |
} | |
object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null); | |
var comparison = ((IComparable)value).CompareTo(otherPropertyValue); | |
if (!IsValid(comparison)) | |
{ | |
if (OtherPropertyDisplayName == null) | |
{ | |
OtherPropertyDisplayName = GetDisplayNameForProperty(otherPropertyInfo); | |
} | |
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); | |
} | |
return null; | |
} | |
private string GetDisplayNameForProperty(PropertyInfo property) | |
{ | |
var attributes = CustomAttributeExtensions.GetCustomAttributes(property, true); | |
var display = attributes.OfType<DisplayAttribute>().FirstOrDefault(); | |
if (display != null) | |
{ | |
return display.GetName(); | |
} | |
return OtherProperty; | |
} | |
private bool IsValid(int comparison) | |
{ | |
var format = ""; | |
switch (_comparison) | |
{ | |
case Comparison.Equal when comparison != 0: | |
format = FormatEqual; | |
break; | |
case Comparison.GreaterThan when comparison != 1: | |
format = FormatGreaterThan; | |
break; | |
case Comparison.GreaterThanOrEqual when comparison == -1: | |
format = FormatGreaterThanOrEqual; | |
break; | |
case Comparison.LessThan when comparison != -1: | |
format = FormatLessThan; | |
break; | |
case Comparison.LessThanOrEqual when comparison == 1: | |
format = FormatLessThanOrEqual; | |
break; | |
case Comparison.NotEqual when comparison == 0: | |
format = FormatNotEqual; | |
break; | |
} | |
if (format != "") | |
{ | |
ErrorMessage = format; | |
return false; | |
} | |
return true; | |
} | |
} | |
public enum Comparison | |
{ | |
Equal, | |
GreaterThan, | |
GreaterThanOrEqual, | |
LessThan, | |
LessThanOrEqual, | |
NotEqual | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment