Reputation: 3874
I'm attempting to create a custom validation method for one of my entities, so I've created a class that inherits from ValidationAttribute
:
public class OneWheelchairPerTrainAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// This is where I need to access the other entity property
}
}
What I'm struggling with is how I access the other properties on the entity. This is my entity:
public class Ticket
{
public int Id { get; set; }
[Required]
public int TimetableId { get; set; }
[Required]
public bool Wheelchair { get; set; }
public virtual Timetable Timetable { get; set; }
}
The validation annotation I'm writing will be applied to the Wheelchair
property, and I need to access the TimetableId
property from within my validation method.
Upvotes: 1
Views: 3711
Reputation: 13578
Another (in my opinion, better) way to validate multiple properties is by doing it on class
level.
This not the exact same situation as in your answer, but it still involves multiple property validation.
Imagine you want to allow a wheelchair to be an id or a new object, but you still want to allow only one:
An example of my ExactlyOneRequired
attribute:
[AttributeUsage(AttributeTargets.Class)]
public class ExactlyOneRequiredAttribute : ValidationAttribute
{
public string FirstPropertyName { get; set; }
public string SecondPropertyName { get; set; }
//Constructor to take in the property names that are supposed to be checked
public ExactlyOneRequiredAttribute(string firstPropertyName, string secondPropertyName)
{
FirstPropertyName = firstPropertyName;
SecondPropertyName = secondPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return new ValidationResult("Object must have a value;");
var neededProperties = validationContext.ObjectType.GetProperties().Where(propertyInfo => propertyInfo.Name == FirstPropertyName || propertyInfo.Name == SecondPropertyName).Take(2).ToArray();
var value1 = neededProperties[0].GetValue(value);
var value2 = neededProperties[1].GetValue(value);
if (value1 == null | value2 == null)
return ValidationResult.Success;
return FailedValidationResult();
}
public override string FormatErrorMessage(string name) => $"One of the fields: '{FirstPropertyName} or {SecondPropertyName}' is required, it is not allowed to set both.";
private ValidationResult FailedValidationResult() => new ValidationResult(FormatErrorMessage(FirstPropertyName), new List<string> {FirstPropertyName, SecondPropertyName});
}
Usage:
[ExactlyOneRequired(nameof(WheelChairId), nameof(WheelChair))]
public class Train
{
public int? WheelChairId { get; set; }
public WheelChair WheelChair { get; set; }
}
You can of course expect as many properties as you want and make it as generic as you want. My point is, instead of string checking on property name within the attribute, injecting the property names is the cleaner way to go,
Upvotes: 6
Reputation: 997
You can use the IsValid overload to pass in a ValidationContext, like this:
public class OneWheelchairPerTrainAttribute : ValidationAttribute
{
public override bool IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
// Here is your timetableId
Object timeTableId = type.GetProperty("TimetableId ").GetValue(instance, null);
//Do validation ...
}
}
Upvotes: 4