I'm trying to perform some validation on my objects that is are not tied to a UI. For example I have these three classes:
public class XDeftable {
[ObjectCollectionValidator(typeof(XSchedGroup))]
public List<XSchedGroup> SCHED_GROUP { get; set; }
}
[IdentifyingProperty("TABLE_NAME")]
public class XSchedGroup {
[ObjectCollectionValidator(typeof(XJob))]
public List<XJob> JOB { get; set; }
[Required]
public string TABLE_NAME { get; set; }
}
[IdentifyingProperty("JOBNAME")]
public class XJob : ICalendar {
[Required]
public string JOBNAME { get; set; }
[Range(-62, 62)]
public string SHIFTNUM { get; set; }
[ObjectCollectionValidator(typeof(XTagNames))]
public List<XTagNames> TAG_NAMES { get; set; }
}
XDeftable -> XSchedGroup -> XJob -> XTagNames
When an object fails validation things work exactly as one would expect but if I simply inspect the ValidationResult for it's Key and Message I end up with something like: "JOBNAME | Field is required."
The problem with this is that considering I might have hundreds of jobs in a single scheduling group the validation is useless since I don't know which particular job failed. I've searched through every bit of documentation I could find regarding validation and C# and haven't found any way of getting more data. I created the attribute IdentifyingProperty to allow me to tag which property of the class identifies the particular instance of the class. I had a previous custom validation solution that I mocked up based off of this Git Repo: https://github.com/reustmd/DataAnnotationsValidatorRecursive/tree/master/DataAnnotationsValidator/DataAnnotationsValidator. It worked alright but I wanted to swap over to something more robust.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class IdentifyingProperty : Attribute {
public string Name { get; set; }
public IdentifyingProperty(string name) {
this.Name = name;
}
}
So far I've been able to come up with the following:
public ValidationResults Validate(XDeftable deftable) {
var results = new ObjectValidator(typeof(XDeftable)).Validate(deftable);
var detailedResults = new ValidationResults();
foreach (var item in results) {
var targetType = item.Target.GetType();
var identProp = targetType.GetCustomAttribute<IdentifyingProperty>();
if (identProp != null) {
var pi = targetType.GetProperty(identProp.Name);
var newKey = String.Format("{0}[{1}].{2}", targetType.Name, pi.GetValue(item.Target).ToString(), item.Key);
detailedResults.AddResult(new ValidationResult(item.Message, item.Target, newKey, item.Tag, item.Validator));
}
else {
detailedResults.AddResult(item);
}
}
return detailedResults;
}
This will at least return me "XJob[JOBNAME].SHIFTNUM | The field SHIFTNUM must be between -62 and 62." I'd still like it if there was a way for me to get results that follow the chain of containers such as: XSchedGroup[TABLE_NAME].XJob[JOBNAME].SHIFTNUM.