I have an EditForm that has three properties, one of which at least must be specified and cannot be empty/white space. To enforce this, I built a custom "RequiredIf" data annotation with the following code inside of the "IsValid" method:
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (value != null)
{
return ValidationResult.Success;
}
int numberOfInvalidProperties = 0;
object instance = validationContext.ObjectInstance;
Type containerType = instance.GetType();
Type type = instance.GetType();
for (int i = 0; i < this.PropertyNames.Length; i )
{
PropertyInfo? otherProperty = validationContext.ObjectType.GetProperty(this.PropertyNames[i]);
if (otherProperty == null)
{
return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", this.PropertyNames[i]));
}
object? propertyValue = otherProperty.GetValue(validationContext.ObjectInstance);
if (propertyValue == this.DesiredValues[i])
{
numberOfInvalidProperties ;
}
else if (otherProperty?.PropertyType == typeof(string))
{
// treat empty strings as null
if (this.DesiredValues[i] == null && string.IsNullOrWhiteSpace((string?)propertyValue))
{
numberOfInvalidProperties ;
}
}
}
if (numberOfInvalidProperties == this.PropertyNames.Length)
{
return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName }!);
}
else
{
return ValidationResult.Success;
}
}
In turn, my model has the following three properties with this custom annotation set:
[RequiredIf(new string[] { "OrderNumber", "InternalOrderNumber" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string CardStatementId { get; set; }
[RequiredIf(new string[] { "OrderNumber", "CardStatementId" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string InternalOrderNumber { get; set; }
[RequiredIf(new string[] { "CardStatementId", "InternalOrderNumber" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string OrderNumber { get; set; }
When I try to submit the form with no value specified on any of the properties, I get the "At least one of these is required" error message to appear on all three. Now here are the two issues I am running into:
- When I specify a valid value in any of those three properties, the error message only goes away from the property who had the value entered for. The error message does not clear off of the other two properties. This does not prevent the form from submitting.
- The second issue is that while even though the "IsValid" code above is checking for whitespace strings, it is not showing any error message even though it does enter the
numberOfInvalidProperties == this.PropertyNames.Length
code branch.
Can anyone spot the issue at play here? Am I missing a "StateHasChanged" call somewhere or are custom validation annotations not bound into the same behavioral context as the built in ones?
CodePudding user response:
I'm speculating here. But do you have a module for purchase order and one of card-statment?
I'm not sure, but i believe when your creating a purchase order, it retrieves a null object. Or p-card-statement id is null. Have you checked what kind of value your getting when your retrieving the data and checking if they are valid.
I would recommend using the deBugger and checking the value for those two. Another thing you might need to check, which i often miss is to use the Include method. If your working with joint tables.
CodePudding user response:
When I specify a valid value in any of those three properties, the error message only goes away from the property who had the value entered for. The error message does not clear off of the other two properties. This does not prevent the form from submitting
This is normal. You have placed validation on individual fields. Well, any field whose value is not validated will show an error message. To fix this problem, it is better to apply your validation to the model. For Example:
public class CustomAnnotationValidator : ValidationAttribute
{
protected override ValidationResult
IsValid(object value, ValidationContext validationContext)
{
OrderModel orderModel = (OrderModel)value;
bool resultFlag = true;
string errorMessage = "";
if (string.IsNullOrEmpty(orderModel.CardStatementId) &&
string.IsNullOrEmpty(orderModel.InternalOrderNumber) &&
string.IsNullOrEmpty(orderModel.OrderNumber))
{
errorMessage = "At least one of CardStatementId,InternalOrderNumber,OrderNumber are required";
resultFlag = false;
}
if (resultFlag)
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(errorMessage);
}
}
}
Now, you can apply the CustomAnnotationValidator
on your order model (viewModel, DTO, ...) as follows:
[CustomAnnotationValidator ]
Public class OrderModel
{
public string CardStatementId { get; set; }
public string InternalOrderNumber { get; set; }
public string OrderNumber { get; set; }
}
Note: These codes are not tested, I just tried to present the method that I use in the form of your codes. I hope it is useful.