5

In my MVC-project I have different custom validation-attributes. One of them is to check the value of a property against the value of another property.

As stated in many articles, I add something like

result.ValidationParameters.Add("otherproperty", _otherPropertyHtml);
result.ValidationParameters.Add("comparetype", _compareType);
result.ValidationParameters.Add("equalitytype", _equalityType);

to the returning ModelClientValidationRule object.

My problem now is, that - if my property to check - is encapsulated in another object, validation will not work.

If I create something like

@Html.TextBoxFor(m => m.ValueOne)
@Html.TextBoxFor(m => m.ValueTwo)

validation will work fine as it renders

data-val-otherproperty="ValueTwo"

My problem is for the following

@Html.TextBoxFor(m => m.IntermediateObject.ValueOne)
@Html.TextBoxFor(m => m.IntermediateObject.ValueTwo)

This will render two textboxes with names IntermediateObject_ValueOne and IntermediateObject.ValueTwo. But still data-val-otherproperty="ValueOne" for the first textbox.

How can it be achieved, that data-val-otherproperty has always the correct name of the other property?

My thoughts are something like HtmlHelper<>.NameFor(m => ...) or something that uses reflection?

Update 1 - Added code as requested by comments

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false)]
public class CustomCompareToOther : ValidationAttribute, IClientValidatable
{
    // private backing-field
    private readonly string _otherPropertyName;

    // constructor
    public OemCompareToOther(string otherPropertyName)
    {
        _otherPropertyName = otherPropertyName;
    }

    // implementation of IClientValidatable
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var result = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(metadata.DisplayName),
                ValidationType = "customcomparetoother"
            };

        // add the property-name so it is known when rendered for client-side validation
        result.ValidationParameters.Add("otherproperty", _otherPropertyHtml); // here I would need IntermediateObject.ValueTwo instead of only ValueTwo

        yield return result;
    }
}

Usage at model-level would be

public class MyModel
{
    [CustomCompareToOther("ValueOTwo", CompareType.NotEqual, PropertyType.String)]
    public string ValueOne { get; set; }    

    [CustomCompareToOther("ValueTwo", CompareType.NotEqual, PropertyType.String)]
    public string ValueTwo { get; set; }
}

And what I will put into my View would be something like

public class ViewModel
{
    public MyModel IntermediateObject { get; set; }
}

used e.g. return View(new ViewModel()). So, in the rendered HTML I would have an input

<input type="text" name="IntermediateObject_ValueOne" id="IntermediateObject.ValueOne" data-val-customcomparetoother-otherpropertyname="ValueTwo" />
<input type="text" name="IntermediateObject_ValueTwo" id="IntermediateObject.ValueTwo" data-val-customcomparetoother-otherpropertyname="ValueOne" />

but I need

<input type="text" name="IntermediateObject_ValueOne" id="IntermediateObject.ValueOne" data-val-customcomparetoother-otherpropertyname="IntermediateObject.ValueTwo" />
<input type="text" name="IntermediateObject_ValueTwo" id="IntermediateObject.ValueTwo" data-val-customcomparetoother-otherpropertyname="IntermediateObject.ValueOne" />

in the html so javascript-validation can fetch the other property correctly.

4
  • why would you want to do this? if you have multiple models that have so much similarity, you should make them share a parent class, then you can make a view based on that parent Commented Aug 4, 2015 at 12:44
  • I have a request/response structure build up where my Response contains the Request that will be posted next. So, in my View I'll render @Html.TextBoxFor(m => m.Request.ValueOne). Sure, I could always add "Request." but I don't like magic strings, not all parts are currently request/response based and if the name changes from Request to RenamedRequest, nothing will work Commented Aug 4, 2015 at 12:48
  • Show all your attribute code Commented Aug 4, 2015 at 14:35
  • @AlexArt. I added the code (just example-code, not working in real) as Update 1 Commented Aug 5, 2015 at 6:37

1 Answer 1

2

You can use the [Compare("PropertyName")] Data Annotation.

Example in your View Model:

[Display(Name = "New Password")]
[DataType(DataType.Password)]
public string NewPassword { get; set; }

[Display(Name = "Confirm Password")]
[DataType(DataType.Password)]
[Compare("NewPassword")]
public string PasswordConfirmation { get; set; }

Just remember to add the System.ComponentModel.DataAnnotations namespace to your using statements

Sign up to request clarification or add additional context in comments.

1 Comment

(For me) there it's no option to change from custom-attribute I currently have to CompareAttribute

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.