I have created a custom CompareLessThan validation attribute by copying the ASP.NET MVC 3 CompareAttribute and instead of checking for equality, I check to see that one property is less than another. If there is a client side error, the message '{0} must be less than {1}' is displayed to the user.
My model is setup as follows with the Display attributes referencing a resource file.
[CompareLessThan("AmountAvailable", ErrorMessageResourceName="CompareLessThan", ErrorMessageResourceType = typeof(Resources.ValidationMessages))]
[Display(Name = "Amount", ResourceType = typeof(Resources.Labels))]
public decimal Amount { get; set; }
[Display(Name = "AmountAvailable", ResourceType = typeof(Resources.Labels))]
public decimal AmountAvailable { get; set; }
Then the custom validation GetClientValidationRules method is exactly the same as in the CompareAttribute
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(OtherProperty), this.AllowEquality);
}
Here we are generating the error message that will be displayed to the user if there is a problem. I can get the display name from the resource file for the property that is decorated with my custom CompareLessThan attribute, but my question is how do I go about getting the display name of the 'other' property we are comparing against? In the IsValid method we have a reference to the validationContext from which I can generate a PropertyInfo object for the 'other' property and I think get the display name. But, in the GetClientValidationRules I don't have access to that.
I could always just pass in another value for the display name of the other property but I was hoping there would be a way to derive it as I'm already specifying it with data annotations.
Any ideas?
The answer provided by nemesv didn't work as the metadata.Model property has a value of 0. But, through the metadata we do have the full name of the model so it is possible to create a new instance of that model and then create a new DataAnnonationsModelMetadataProvider from that create instance. From there we can get the display name of the other property.
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
Type type = Type.GetType(metadata.ContainerType.FullName);
var model = Activator.CreateInstance(type);
var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => model, type, this.OtherProperty);
this.otherPropertyDisplayName = otherMetaData.DisplayName;
yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(this.OtherProperty), this.AllowEquality);
}
I really don't like this solution (even though it works) as it seems there should be a better way. Does anyone else have any other ideas?