I am trying to validate that either of 2 textbox fields in my view have a value supplied. I made this Model Validator:
public class RequireEitherValidator : ModelValidator
{
private readonly string compareProperty;
private readonly string errorMessage;
public RequireEitherValidator(ModelMetadata metadata,
ControllerContext context, string compareProperty, string errorMessage)
: base(metadata, context)
{
this.compareProperty = compareProperty;
this.errorMessage = errorMessage;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (Metadata.Model == null)
yield break;
var propertyInfo = container.GetType().GetProperty(compareProperty);
if (propertyInfo == null)
throw new InvalidOperationException("Unknown property:" + compareProperty);
string valueToCompare = propertyInfo.GetValue(container, null).ToString();
if (string.IsNullOrEmpty(Metadata.Model.ToString()) && string.IsNullOrEmpty(valueToCompare))
yield return new ModelValidationResult
{
Message = errorMessage
};
}
}
This validation logic never gets hit and I think it’s because no value gets supplied to the textboxes.
In case you need it, here’s the provider and attribute I created along with the attribute usage:
public class MyValidatorProvider : AssociatedValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(
ModelMetadata metadata, ControllerContext context,
IEnumerable<Attribute> attributes)
{
foreach (var attrib in attributes.OfType<RequireEitherAttribute>())
yield return new RequireEitherValidator(metadata, context,
attrib.CompareProperty, attrib.ErrorMessage);
}
}
public class RequireEitherAttribute : Attribute
{
public readonly string CompareProperty;
public string ErrorMessage { get; set; }
public RequireEitherAttribute(string compareProperty)
{
CompareProperty = compareProperty;
}
}
public class StudentLogin
{
[DisplayName("Last Name")]
[Required(ErrorMessage = "You must supply your last name.")]
public string LastName { get; set; }
[DisplayName("Student ID")]
[RegularExpression(@"^\d{1,8}$", ErrorMessage = "Invalid Student ID")]
[RequireEither("SSN", ErrorMessage = "You must supply your student id or social security number.")]
public int? StudentId { get; set; }
[DisplayName("Social Security Number")]
[RegularExpression(@"^\d{9}|\d{3}-\d{2}-\d{4}$", ErrorMessage = "Invalid Social Security Number")]
public string SSN { get; set; }
}
My view:
<%Html.BeginForm(); %>
<p>
Please supply the following information to login:</p>
<ol class="standard">
<li>
<p>
<%=Html.LabelFor(x => x.LastName) %><br />
<%=Html.TextBoxFor(x => x.LastName)%>
<%=Html.ValidationMessageFor(x => x.LastName) %></p>
</li>
<li>
<p>
<%=Html.LabelFor(x => x.StudentId) %><br />
<%=Html.TextBoxFor(x => x.StudentId) %>
<%=Html.ValidationMessageFor(x => x.StudentId) %></p>
<p style="margin-left: 4em;">
- OR -</p>
<p>
<%=Html.LabelFor(x => x.SSN)%><br />
<%=Html.TextBoxFor(x => x.SSN) %>
<%=Html.ValidationMessageFor(x => x.SSN) %>
</p>
</li>
</ol>
<%=Html.SubmitButton("submit", "Login") %>
<%Html.EndForm(); %>
One way to approach this is not just create a ValidationAttribute and apply this at the class level.
The error message will automatically show up in the Validation Summary. The attribute would look something like this (I’ve drastically simplified the validation logic inside IsValid() by treating everything as strings just for brevity:
Note that in this case the object passed to IsValid() is the instance of the class itself rather than the property.