I have the following ViewModel:
public class StayDetails
{
public int NumberOfRooms { get; set; }
public IList<RoomDetail> Rooms { get;set; }
}
public class RoomDetail
{
public int RoomNumber { get; set; }
[MinIfRoomRequired("StayDetails.NumberOfRooms", "RoomNumber", 1]
public int NumberOfAdults { get;set; }
}
What I am trying to do is create a custom validator which will validate the number of adults in a room and make sure that there is at least 1, but only if the current room is required. This is known by looking at the NumberOfRooms property on the StayDetails object.
My custom validator so far:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
// get a reference to the depended properties
var containerType = validationContext.ObjectInstance.GetType();
var requiredRoomsField = containerType.GetProperty(RequiredRoomsPropertyName);
var roomNumberField = containerType.GetProperty(RoomNumberPropertyName);
if (requiredRoomsField != null && roomNumberField != null)
{
// get the value of the dependent properties
var requiredRoomsValue = requiredRoomsField.GetValue(validationContext.ObjectInstance, null);
var roomNumberValue = roomNumberField.GetValue(validationContext.ObjectInstance, null);
... (remaining logic to test values) ...
The problem I have is that I cannot access the NumberOfRooms property, the validationContext.ObjectInstance does not have any refernece to the parent object. I thought about adding a reference to the StayDetails object onto the RoomDetails object during object initialzation so I can reference the property from there but model binding wont allow that as the RoomDetail object does not have a parameterless constructor.
Any suggestions?
Many thanks,
David
I was able to solve this problem by using custom binders. You will need to add a property to refer back to the parent object, such as, NumberOfRooms. In my case, I actually created a delegate that referred back to a routine in the parent object. Apologies in advance for the VB code and formatting issues I'm having with stackoverflow.
For example:
Note that MetadataList is simply
Next I create a custom binder for the parent object: This also does a couple things:
a) Store the parent object in the controllercontext so that it can be used by the child object. b) Re-validate the child object.
Public Class StayDetails_Binder Inherits DefaultModelBinder
End Class
Decorate your subclass appropriately
<ModelBinder(GetType(RoomDetail_Binder))> public class RoomDetail
Set your controller so that it will use the custom binder:
<ModelBinder(GetType(StayDetails_Binder))> (your parameter name here)
Make certain your validator is set to SUCCESS if you validate it without the NumberOfRooms property filled. The binding will execute your validators twice for your child class. First time before the property filled, and then again when the property after it's filled.
You can try doing it with the FluentValidation PropertyValidator.
Write less do more...
You should define validation annotation on
StayDetails class
instead of RoomDetail. This way you will have all the values, NumberOfRooms, list of rooms and their respective RoomNumber and NumberOfAdults. Change your validator accordingly.