ASP.NET MVC ValidationAttribute Get Other Property Display Name
Asked Answered
C

3

7

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?

Colleague answered 13/10, 2011 at 23:39 Comment(0)
C
6

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?

Colleague answered 14/10, 2011 at 18:13 Comment(0)
S
7

As of ASP.NET MVC 4 this is how I managed to get the other property:

PropertyInfo otherPropertyInfo =
                  this.Metadata.ContainerType.GetProperty(attribute.DependentProperty);

Then I got the Display attribute from the property:

var displayAttribute =
    otherPropertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).
    FirstOrDefault() as DisplayProperty;

In your case:

// GetName() is important to get the translated name if you're using a resource file...
this.otherPropertyDisplayName = displayAttribute.GetName();

GetName() reference:

http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.name%28v=vs.95%29.aspx

Selfstarter answered 25/6, 2012 at 21:11 Comment(2)
Was that meant to be "as DisplayProperty" or "as DisplayAttribute"? Mine didn't work for DisplayProperty but worked fine for DisplayAttribute. Great solution otherwise.Educable
@MVCKarl: I think you're right... Maybe it was a bit of confusion while typing the answer here. I can't record what I did exactly at that time. :)Selfstarter
C
6

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?

Colleague answered 14/10, 2011 at 18:13 Comment(0)
V
4

I haven't tried it out but you can get the model properties with the metadata.Properties property

metadata.Properties.Single(p => p.PropertyName == "OtherPropName").DisplayName;

EDIT: Because Properties is empty what you can always do (although it's very elegant). You can generate the metadata for yourself.

var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => metaData.Model, metaData.ModelType, "OtherPropertyName");
Verwoerd answered 14/10, 2011 at 5:11 Comment(3)
Good thought but the Properties collection is always empty.Colleague
@NickOlsen That is sad. I've update my answer with some "workaround".Verwoerd
That doesn't work either as the metadata.Model property is just the value 0. Using your logic I was able to figure out another way to do it (see other answer) but I don't really like it. I'm hoping there is a better way that someone else can provide.Colleague

© 2022 - 2024 — McMap. All rights reserved.